aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--MAINTAINERS32
-rw-r--r--Makefile.objs3
-rw-r--r--arch_init.c52
-rw-r--r--backends/rng-random.c4
-rw-r--r--block.c1029
-rw-r--r--block/Makefile.objs2
-rw-r--r--block/backup.c7
-rw-r--r--block/blkdebug.c91
-rw-r--r--block/blkverify.c33
-rw-r--r--block/commit.c8
-rw-r--r--block/cow.c3
-rw-r--r--block/curl.c83
-rw-r--r--block/gluster.c320
-rw-r--r--block/iscsi.c478
-rw-r--r--block/mirror.c117
-rw-r--r--block/nbd-client.c385
-rw-r--r--block/nbd-client.h50
-rw-r--r--block/nbd.c383
-rw-r--r--block/qapi.c114
-rw-r--r--block/qcow.c3
-rw-r--r--block/qcow2.c15
-rw-r--r--block/qcow2.h6
-rw-r--r--block/qed.c15
-rw-r--r--block/raw-posix.c104
-rw-r--r--block/raw-win32.c46
-rw-r--r--block/rbd.c132
-rw-r--r--block/sheepdog.c42
-rw-r--r--block/stream.c2
-rw-r--r--block/vhdx-log.c13
-rw-r--r--block/vhdx.c24
-rw-r--r--block/vhdx.h5
-rw-r--r--block/vmdk.c214
-rw-r--r--block/vvfat.c45
-rw-r--r--blockdev.c129
-rwxr-xr-xconfigure402
-rw-r--r--cpu-exec.c50
-rw-r--r--cpus.c5
-rw-r--r--cputlb.c32
-rw-r--r--default-configs/aarch64-linux-user.mak3
-rw-r--r--default-configs/aarch64-softmmu.mak6
-rw-r--r--default-configs/arm-softmmu.mak5
-rw-r--r--device_tree.c64
-rw-r--r--exec.c354
-rw-r--r--fpu/softfloat.c1055
-rw-r--r--gdb-xml/aarch64-fpu.xml86
-rw-r--r--hmp-commands.hx46
-rw-r--r--hmp.c91
-rw-r--r--hmp.h3
-rw-r--r--hw/9pfs/virtio-9p-device.c43
-rw-r--r--hw/acpi/core.c18
-rw-r--r--hw/acpi/ich9.c24
-rw-r--r--hw/acpi/piix4.c33
-rw-r--r--hw/alpha/typhoon.c2
-rw-r--r--hw/arm/Makefile.objs3
-rw-r--r--hw/arm/allwinner-a10.c103
-rw-r--r--hw/arm/boot.c217
-rw-r--r--hw/arm/cubieboard.c69
-rw-r--r--hw/arm/digic.c115
-rw-r--r--hw/arm/digic_boards.c162
-rw-r--r--hw/arm/highbank.c40
-rw-r--r--hw/arm/mainstone.c15
-rw-r--r--hw/arm/versatilepb.c1
-rw-r--r--hw/arm/vexpress.c62
-rw-r--r--hw/arm/virt.c106
-rw-r--r--hw/arm/xilinx_zynq.c38
-rw-r--r--hw/arm/z2.c2
-rw-r--r--hw/audio/hda-codec.c18
-rw-r--r--hw/audio/intel-hda.c4
-rw-r--r--hw/audio/marvell_88w8618.c2
-rw-r--r--hw/audio/pcspk.c3
-rw-r--r--hw/audio/pl041.c1
-rw-r--r--hw/block/dataplane/virtio-blk.c116
-rw-r--r--hw/block/dataplane/virtio-blk.h5
-rw-r--r--hw/block/fdc.c1
-rw-r--r--hw/block/pflash_cfi01.c260
-rw-r--r--hw/block/virtio-blk.c49
-rw-r--r--hw/char/Makefile.objs1
-rw-r--r--hw/char/cadence_uart.c153
-rw-r--r--hw/char/digic-uart.c195
-rw-r--r--hw/char/exynos4210_uart.c6
-rw-r--r--hw/char/virtio-serial-bus.c33
-rw-r--r--hw/core/qdev-properties-system.c8
-rw-r--r--hw/core/qdev-properties.c40
-rw-r--r--hw/core/qdev.c82
-rw-r--r--hw/core/sysbus.c7
-rw-r--r--hw/cpu/icc_bus.c14
-rw-r--r--hw/display/pl110.c1
-rw-r--r--hw/display/qxl.c10
-rw-r--r--hw/dma/pl080.c1
-rw-r--r--hw/dma/sparc32_dma.c2
-rw-r--r--hw/dma/xilinx_axidma.c13
-rw-r--r--hw/gpio/omap_gpio.c4
-rw-r--r--hw/i2c/omap_i2c.c2
-rw-r--r--hw/i2c/smbus_eeprom.c2
-rw-r--r--hw/i2c/smbus_ich9.c6
-rw-r--r--hw/i386/acpi-build.c8
-rw-r--r--hw/i386/acpi-dsdt-cpu-hotplug.dsl1
-rw-r--r--hw/i386/acpi-dsdt.dsl2
-rw-r--r--hw/i386/acpi-dsdt.hex.generated4
-rw-r--r--hw/i386/kvm/apic.c14
-rw-r--r--hw/i386/kvm/clock.c1
-rw-r--r--hw/i386/kvm/ioapic.c8
-rw-r--r--hw/i386/kvmvapic.c9
-rw-r--r--hw/i386/pc.c63
-rw-r--r--hw/i386/pc_piix.c52
-rw-r--r--hw/i386/pc_q35.c34
-rw-r--r--hw/i386/pc_sysfw.c105
-rw-r--r--hw/i386/q35-acpi-dsdt.dsl6
-rw-r--r--hw/i386/q35-acpi-dsdt.hex.generated4
-rw-r--r--hw/i386/smbios.c14
-rw-r--r--hw/ide/core.c2
-rw-r--r--hw/ide/piix.c3
-rw-r--r--hw/ide/via.c1
-rw-r--r--hw/input/pckbd.c1
-rw-r--r--hw/input/pxa2xx_keypad.c6
-rw-r--r--hw/input/vmmouse.c3
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/allwinner-a10-pic.c200
-rw-r--r--hw/intc/apic.c48
-rw-r--r--hw/intc/apic_common.c79
-rw-r--r--hw/intc/arm_gic.c28
-rw-r--r--hw/intc/arm_gic_common.c5
-rw-r--r--hw/intc/arm_gic_kvm.c1
-rw-r--r--hw/intc/etraxfs_pic.c4
-rw-r--r--hw/intc/gic_internal.h7
-rw-r--r--hw/intc/grlib_irqmp.c2
-rw-r--r--hw/intc/i8259_common.c8
-rw-r--r--hw/intc/ioapic.c13
-rw-r--r--hw/intc/ioapic_common.c16
-rw-r--r--hw/intc/omap_intc.c4
-rw-r--r--hw/intc/pl190.c1
-rw-r--r--hw/intc/xics.c24
-rw-r--r--hw/isa/isa-bus.c1
-rw-r--r--hw/isa/lpc_ich9.c7
-rw-r--r--hw/isa/piix4.c6
-rw-r--r--hw/isa/vt82c686.c6
-rw-r--r--hw/microblaze/Makefile.objs1
-rw-r--r--hw/microblaze/boot.c12
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c9
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c9
-rw-r--r--hw/microblaze/pic_cpu.c47
-rw-r--r--hw/microblaze/pic_cpu.h8
-rw-r--r--hw/mips/gt64xxx_pci.c6
-rw-r--r--hw/misc/arm_l2x0.c1
-rw-r--r--hw/misc/exynos4210_pmu.c3
-rw-r--r--hw/misc/vmport.c3
-rw-r--r--hw/net/etraxfs_eth.c2
-rw-r--r--hw/net/lance.c2
-rw-r--r--hw/net/virtio-net.c36
-rw-r--r--hw/net/xilinx_axienet.c13
-rw-r--r--hw/nvram/fw_cfg.c1
-rw-r--r--hw/nvram/spapr_nvram.c16
-rw-r--r--hw/pci-bridge/dec.c6
-rw-r--r--hw/pci-host/apb.c6
-rw-r--r--hw/pci-host/bonito.c8
-rw-r--r--hw/pci-host/grackle.c10
-rw-r--r--hw/pci-host/piix.c56
-rw-r--r--hw/pci-host/ppce500.c5
-rw-r--r--hw/pci-host/prep.c7
-rw-r--r--hw/pci-host/q35.c32
-rw-r--r--hw/pci-host/uninorth.c28
-rw-r--r--hw/pci-host/versatile.c6
-rw-r--r--hw/pci/pci.c44
-rw-r--r--hw/pci/pci_bridge.c6
-rw-r--r--hw/ppc/e500.c213
-rw-r--r--hw/ppc/e500plat.c6
-rw-r--r--hw/ppc/mpc8544ds.c6
-rw-r--r--hw/ppc/ppc440_bamboo.c24
-rw-r--r--hw/ppc/ppc4xx_pci.c5
-rw-r--r--hw/ppc/spapr.c49
-rw-r--r--hw/ppc/spapr_events.c6
-rw-r--r--hw/ppc/spapr_pci.c44
-rw-r--r--hw/ppc/spapr_rtas.c97
-rw-r--r--hw/ppc/spapr_vio.c14
-rw-r--r--hw/ppc/virtex_ml507.c2
-rw-r--r--hw/s390x/ipl.c1
-rw-r--r--hw/s390x/s390-virtio-bus.c2
-rw-r--r--hw/s390x/virtio-ccw.c90
-rw-r--r--hw/s390x/virtio-ccw.h1
-rw-r--r--hw/scsi/scsi-bus.c16
-rw-r--r--hw/scsi/scsi-disk.c52
-rw-r--r--hw/scsi/scsi-generic.c2
-rw-r--r--hw/scsi/vhost-scsi.c45
-rw-r--r--hw/scsi/virtio-scsi.c63
-rw-r--r--hw/sd/pl181.c1
-rw-r--r--hw/sh4/sh_pci.c6
-rw-r--r--hw/timer/Makefile.objs3
-rw-r--r--hw/timer/allwinner-a10-pit.c254
-rw-r--r--hw/timer/arm_mptimer.c1
-rw-r--r--hw/timer/digic-timer.c163
-rw-r--r--hw/timer/hpet.c30
-rw-r--r--hw/timer/i8254_common.c7
-rw-r--r--hw/timer/m48t59.c3
-rw-r--r--hw/timer/mc146818rtc.c3
-rw-r--r--hw/timer/pl031.c1
-rw-r--r--hw/usb/Makefile.objs2
-rw-r--r--hw/usb/bus.c2
-rw-r--r--hw/usb/desc-msos.c234
-rw-r--r--hw/usb/desc.c37
-rw-r--r--hw/usb/desc.h11
-rw-r--r--hw/usb/dev-hid.c8
-rw-r--r--hw/virtio/dataplane/Makefile.objs2
-rw-r--r--hw/virtio/dataplane/hostmem.c183
-rw-r--r--hw/virtio/dataplane/vring.c253
-rw-r--r--hw/virtio/virtio-balloon.c32
-rw-r--r--hw/virtio/virtio-bus.c80
-rw-r--r--hw/virtio/virtio-mmio.c9
-rw-r--r--hw/virtio/virtio-pci.c122
-rw-r--r--hw/virtio/virtio-pci.h1
-rw-r--r--hw/virtio/virtio-rng.c43
-rw-r--r--hw/virtio/virtio.c42
-rw-r--r--hw/watchdog/watchdog.c3
-rw-r--r--hw/xen/xen_apic.c6
-rw-r--r--include/block/block.h44
-rw-r--r--include/block/block_int.h70
-rw-r--r--include/block/qapi.h1
-rw-r--r--include/exec/cpu-all.h3
-rw-r--r--include/exec/memory-internal.h90
-rw-r--r--include/exec/memory.h12
-rw-r--r--include/exec/ram_addr.h147
-rw-r--r--include/fpu/softfloat.h96
-rw-r--r--include/hw/acpi/acpi.h8
-rw-r--r--include/hw/arm/allwinner-a10.h35
-rw-r--r--include/hw/arm/digic.h43
-rw-r--r--include/hw/arm/pxa.h8
-rw-r--r--include/hw/boards.h7
-rw-r--r--include/hw/char/digic-uart.h47
-rw-r--r--include/hw/cpu/icc_bus.h2
-rw-r--r--include/hw/i386/apic_internal.h2
-rw-r--r--include/hw/i386/ioapic_internal.h3
-rw-r--r--include/hw/i386/pc.h48
-rw-r--r--include/hw/i386/smbios.h2
-rw-r--r--include/hw/intc/allwinner-a10-pic.h40
-rw-r--r--include/hw/intc/arm_gic_common.h2
-rw-r--r--include/hw/pci-host/q35.h2
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/ppc/spapr.h9
-rw-r--r--include/hw/ptimer.h4
-rw-r--r--include/hw/qdev-core.h30
-rw-r--r--include/hw/qdev-properties.h17
-rw-r--r--include/hw/timer/allwinner-a10-pit.h58
-rw-r--r--include/hw/timer/digic-timer.h46
-rw-r--r--include/hw/timer/hpet.h10
-rw-r--r--include/hw/usb.h3
-rw-r--r--include/hw/virtio/dataplane/hostmem.h58
-rw-r--r--include/hw/virtio/dataplane/vring.h10
-rw-r--r--include/hw/virtio/virtio-bus.h22
-rw-r--r--include/hw/virtio/virtio-rng.h2
-rw-r--r--include/hw/virtio/virtio-scsi.h4
-rw-r--r--include/hw/virtio/virtio.h8
-rw-r--r--include/hw/xilinx.h14
-rw-r--r--include/migration/migration.h11
-rw-r--r--include/migration/qemu-file.h4
-rw-r--r--include/migration/vmstate.h10
-rw-r--r--include/monitor/monitor.h5
-rw-r--r--include/qapi/error.h6
-rw-r--r--include/qapi/qmp/qdict.h1
-rw-r--r--include/qapi/qmp/qerror.h1
-rw-r--r--include/qapi/visitor.h3
-rw-r--r--include/qemu-io.h3
-rw-r--r--include/qemu/bitmap.h86
-rw-r--r--include/qemu/bitops.h14
-rw-r--r--include/qemu/config-file.h6
-rw-r--r--include/qemu/host-utils.h32
-rw-r--r--include/qemu/option.h1
-rw-r--r--include/qemu/osdep.h2
-rw-r--r--include/qemu/readline.h (renamed from include/monitor/readline.h)20
-rw-r--r--include/qemu/typedefs.h2
-rw-r--r--include/qom/object.h4
-rw-r--r--include/sysemu/blockdev.h2
-rw-r--r--include/sysemu/char.h10
-rw-r--r--include/sysemu/device_tree.h80
-rw-r--r--include/sysemu/qtest.h25
-rw-r--r--include/ui/qemu-spice.h2
-rw-r--r--kvm-all.c28
-rw-r--r--libcacard/vscclient.c10
-rw-r--r--linux-user/aarch64/syscall.h1
-rw-r--r--linux-user/aarch64/target_cpu.h5
-rw-r--r--linux-user/arm/target_cpu.h2
-rw-r--r--linux-user/elfload.c3
-rw-r--r--linux-user/flatload.c3
-rw-r--r--linux-user/linuxload.c4
-rw-r--r--linux-user/main.c154
-rw-r--r--linux-user/qemu.h6
-rw-r--r--linux-user/signal.c24
-rw-r--r--linux-user/syscall.c16
-rw-r--r--linux-user/syscall_defs.h1
-rw-r--r--memory.c18
-rw-r--r--migration.c34
-rw-r--r--monitor.c41
-rw-r--r--nbd.c2
-rw-r--r--net/net.c2
-rw-r--r--qapi-schema.json226
-rw-r--r--qdev-monitor.c17
-rw-r--r--qemu-char.c10
-rw-r--r--qemu-doc.texi23
-rw-r--r--qemu-file.c826
-rw-r--r--qemu-img.c28
-rw-r--r--qemu-img.texi23
-rw-r--r--qemu-io-cmds.c57
-rw-r--r--qemu-io.c119
-rw-r--r--qemu-options.hx4
-rw-r--r--qemu-seccomp.c8
-rw-r--r--qmp-commands.hx168
-rw-r--r--qmp.c78
-rw-r--r--qobject/qdict.c91
-rw-r--r--qobject/qerror.c8
-rw-r--r--qom/cpu.c6
-rw-r--r--qom/object.c61
-rw-r--r--qtest.c20
-rw-r--r--savevm.c1512
-rw-r--r--scripts/qapi.py2
-rw-r--r--spice-qemu-char.c11
-rw-r--r--target-arm/Makefile.objs5
-rw-r--r--target-arm/cpu-qom.h1
-rw-r--r--target-arm/cpu.c60
-rw-r--r--target-arm/cpu.h222
-rw-r--r--target-arm/cpu64.c20
-rw-r--r--target-arm/crypto_helper.c281
-rw-r--r--target-arm/gdbstub64.c4
-rw-r--r--target-arm/helper-a64.c124
-rw-r--r--target-arm/helper-a64.h28
-rw-r--r--target-arm/helper.c512
-rw-r--r--target-arm/helper.h47
-rw-r--r--target-arm/kvm-consts.h37
-rw-r--r--target-arm/kvm.c495
-rw-r--r--target-arm/kvm32.c515
-rw-r--r--target-arm/kvm64.c204
-rw-r--r--target-arm/machine.c12
-rw-r--r--target-arm/neon_helper.c12
-rw-r--r--target-arm/translate-a64.c4365
-rw-r--r--target-arm/translate.c214
-rw-r--r--target-arm/translate.h27
-rw-r--r--target-i386/cpu-qom.h6
-rw-r--r--target-i386/cpu.c112
-rw-r--r--target-i386/cpu.h4
-rw-r--r--target-i386/helper.c18
-rw-r--r--target-i386/kvm.c23
-rw-r--r--target-i386/misc_helper.c8
-rw-r--r--target-i386/translate.c2645
-rw-r--r--target-microblaze/cpu.c21
-rw-r--r--target-microblaze/cpu.h4
-rw-r--r--target-microblaze/translate.c139
-rw-r--r--target-mips/translate.c119
-rw-r--r--target-openrisc/translate.c2
-rw-r--r--target-ppc/cpu-models.c3
-rw-r--r--target-ppc/cpu-models.h7
-rw-r--r--target-ppc/cpu-qom.h2
-rw-r--r--target-ppc/cpu.h9
-rw-r--r--target-ppc/excp_helper.c5
-rw-r--r--target-ppc/helper_regs.h2
-rw-r--r--target-ppc/kvm.c32
-rw-r--r--target-ppc/translate.c624
-rw-r--r--target-ppc/translate_init.c91
-rw-r--r--target-s390x/cpu.h3
-rw-r--r--target-s390x/ioinst.c1
-rw-r--r--target-s390x/kvm.c98
-rw-r--r--target-sh4/translate.c167
-rw-r--r--target-sparc/translate.c1
-rw-r--r--tcg/i386/tcg-target.c2
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile12
-rw-r--r--tests/acpi-test.c397
-rw-r--r--tests/check-qdict.c156
-rw-r--r--tests/check-qom-interface.c105
-rw-r--r--tests/fdc-test.c5
-rw-r--r--tests/i440fx-test.c167
-rw-r--r--tests/ide-test.c3
-rw-r--r--tests/qdev-monitor-test.c4
-rwxr-xr-xtests/qemu-iotests/0171
-rwxr-xr-xtests/qemu-iotests/0181
-rwxr-xr-xtests/qemu-iotests/0193
-rwxr-xr-xtests/qemu-iotests/0203
-rwxr-xr-xtests/qemu-iotests/0343
-rwxr-xr-xtests/qemu-iotests/0373
-rwxr-xr-xtests/qemu-iotests/04074
-rw-r--r--tests/qemu-iotests/051.out3
-rwxr-xr-xtests/qemu-iotests/05930
-rw-r--r--tests/qemu-iotests/059.out84
-rwxr-xr-xtests/qemu-iotests/0633
-rwxr-xr-xtests/qemu-iotests/0691
-rwxr-xr-xtests/qemu-iotests/071239
-rw-r--r--tests/qemu-iotests/071.out90
-rwxr-xr-xtests/qemu-iotests/07269
-rw-r--r--tests/qemu-iotests/072.out21
-rwxr-xr-xtests/qemu-iotests/077278
-rw-r--r--tests/qemu-iotests/077.out202
-rw-r--r--tests/qemu-iotests/common.rc28
-rw-r--r--tests/qemu-iotests/group3
-rw-r--r--tests/qom-test.c2
-rw-r--r--tests/test-vmstate.c357
-rw-r--r--trace-events1
-rw-r--r--translate-all.c37
-rw-r--r--translate-all.h7
-rw-r--r--ui/cocoa.m103
-rw-r--r--ui/gtk.c18
-rw-r--r--ui/spice-core.c22
-rw-r--r--ui/spice-display.c17
-rw-r--r--util/Makefile.objs1
-rw-r--r--util/bitmap.c60
-rw-r--r--util/error.c22
-rw-r--r--util/oslib-posix.c23
-rw-r--r--util/oslib-win32.c19
-rw-r--r--util/qemu-config.c102
-rw-r--r--util/qemu-option.c9
-rw-r--r--util/qemu-progress.c11
-rw-r--r--util/qemu-sockets.c18
-rw-r--r--util/readline.c (renamed from readline.c)46
-rw-r--r--vl.c42
-rw-r--r--vmstate.c650
411 files changed, 22657 insertions, 8971 deletions
diff --git a/.travis.yml b/.travis.yml
index 90f167630a..c7ff4da29c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,6 +16,7 @@ env:
matrix:
- TARGETS=alpha-softmmu,alpha-linux-user
- TARGETS=arm-softmmu,arm-linux-user
+ - TARGETS=aarch64-softmmu,aarch64-linux-user
- TARGETS=cris-softmmu
- TARGETS=i386-softmmu,x86_64-softmmu
- TARGETS=lm32-softmmu
diff --git a/MAINTAINERS b/MAINTAINERS
index 7eed206101..fb5324285c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -219,6 +219,13 @@ F: *win32*
ARM Machines
------------
+Allwinner-a10
+M: Li Guang <lig.fnst@cn.fujitsu.com>
+S: Maintained
+F: hw/*/allwinner-a10*
+F: include/hw/*/allwinner-a10*
+F: hw/arm/cubieboard.c
+
Exynos
M: Evgeny Voevodin <e.voevodin@samsung.com>
M: Maksim Kozlov <m.kozlov@samsung.com>
@@ -233,6 +240,12 @@ S: Supported
F: hw/arm/highbank.c
F: hw/net/xgmac.c
+Canon DIGIC
+M: Antony Pavlov <antonynpavlov@gmail.com>
+S: Maintained
+F: include/hw/arm/digic.h
+F: hw/*/digic*
+
Gumstix
M: qemu-devel@nongnu.org
S: Orphan
@@ -500,9 +513,23 @@ X86 Machines
------------
PC
M: Anthony Liguori <aliguori@amazon.com>
+M: Michael S. Tsirkin <mst@redhat.com>
S: Supported
-F: hw/i386/pc.[ch]
-F: hw/i386/pc_piix.c
+F: include/hw/i386/
+F: hw/i386/
+F: hw/pci-host/piix.c
+F: hw/pci-host/q35.c
+F: hw/pci-host/pam.c
+F: include/hw/pci-host/q35.h
+F: include/hw/pci-host/pam.h
+F: hw/isa/piix4.c
+F: hw/isa/lpc_ich9.c
+F: hw/i2c/smbus_ich9.c
+F: hw/acpi/piix4.c
+F: hw/acpi/ich9.c
+F: include/hw/acpi/ich9.h
+F: include/hw/acpi/piix.h
+
Xtensa Machines
---------------
@@ -687,6 +714,7 @@ F: ui/
Cocoa graphics
M: Andreas Färber <andreas.faerber@web.de>
+M: Peter Maydell <peter.maydell@linaro.org>
S: Odd Fixes
F: ui/cocoa.m
diff --git a/Makefile.objs b/Makefile.objs
index 2b6c1fe2a8..ac1d0e1c28 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -43,7 +43,6 @@ libcacard-y += libcacard/vcardt.o
ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/
common-obj-y += net/
-common-obj-y += readline.o
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o
@@ -51,6 +50,8 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += migration.o migration-tcp.o
+common-obj-y += vmstate.o
+common-obj-y += qemu-file.o
common-obj-$(CONFIG_RDMA) += migration-rdma.o
common-obj-y += qemu-char.o #aio.o
common-obj-y += block-migration.o
diff --git a/arch_init.c b/arch_init.c
index e0acbc5661..77912e7a7d 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -48,7 +48,9 @@
#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"
#ifdef DEBUG_ARCH_INIT
#define DPRINTF(fmt, ...) \
@@ -359,11 +361,10 @@ ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
return (next - base) << TARGET_PAGE_BITS;
}
-static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
- ram_addr_t offset)
+static inline bool migration_bitmap_set_dirty(ram_addr_t addr)
{
bool ret;
- int nr = (mr->ram_addr + offset) >> TARGET_PAGE_BITS;
+ int nr = addr >> TARGET_PAGE_BITS;
ret = test_and_set_bit(nr, migration_bitmap);
@@ -373,12 +374,47 @@ static inline bool migration_bitmap_set_dirty(MemoryRegion *mr,
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);
+ }
+ }
+ }
+}
+
+
/* Needs iothread lock! */
static void migration_bitmap_sync(void)
{
RAMBlock *block;
- ram_addr_t addr;
uint64_t num_dirty_pages_init = migration_dirty_pages;
MigrationState *s = migrate_get_current();
static int64_t start_time;
@@ -399,13 +435,7 @@ static void migration_bitmap_sync(void)
address_space_sync_dirty_bitmap(&address_space_memory);
QTAILQ_FOREACH(block, &ram_list.blocks, next) {
- for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
- if (memory_region_test_and_clear_dirty(block->mr,
- addr, TARGET_PAGE_SIZE,
- DIRTY_MEMORY_MIGRATION)) {
- migration_bitmap_set_dirty(block->mr, addr);
- }
- }
+ migration_bitmap_sync_range(block->mr->ram_addr, block->length);
}
trace_migration_bitmap_sync_end(migration_dirty_pages
- num_dirty_pages_init);
diff --git a/backends/rng-random.c b/backends/rng-random.c
index 68dfc8a9c6..136499d305 100644
--- a/backends/rng-random.c
+++ b/backends/rng-random.c
@@ -123,15 +123,15 @@ static void rng_random_init(Object *obj)
NULL);
s->filename = g_strdup("/dev/random");
+ s->fd = -1;
}
static void rng_random_finalize(Object *obj)
{
RndRandom *s = RNG_RANDOM(obj);
- qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
-
if (s->fd != -1) {
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
qemu_close(s->fd);
}
diff --git a/block.c b/block.c
index 13f001ad69..cb21a5fa61 100644
--- a/block.c
+++ b/block.c
@@ -32,6 +32,7 @@
#include "sysemu/sysemu.h"
#include "qemu/notify.h"
#include "block/coroutine.h"
+#include "block/qapi.h"
#include "qmp-commands.h"
#include "qemu/timer.h"
@@ -69,11 +70,11 @@ static int coroutine_fn bdrv_co_readv_em(BlockDriverState *bs,
static int coroutine_fn bdrv_co_writev_em(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
QEMUIOVector *iov);
-static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
+static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
+ int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
-static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
+static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
+ int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
int64_t sector_num,
@@ -90,6 +91,9 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
QTAILQ_HEAD_INITIALIZER(bdrv_states);
+static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
+ QTAILQ_HEAD_INITIALIZER(graph_bdrv_states);
+
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
QLIST_HEAD_INITIALIZER(bdrv_drivers);
@@ -188,7 +192,7 @@ void bdrv_io_limits_enable(BlockDriverState *bs)
* @is_write: is the IO a write
*/
static void bdrv_io_limits_intercept(BlockDriverState *bs,
- int nb_sectors,
+ unsigned int bytes,
bool is_write)
{
/* does this io must wait */
@@ -201,9 +205,8 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
}
/* the IO will be executed, do the accounting */
- throttle_account(&bs->throttle_state,
- is_write,
- nb_sectors * BDRV_SECTOR_SIZE);
+ 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)) {
@@ -214,6 +217,16 @@ static void bdrv_io_limits_intercept(BlockDriverState *bs,
qemu_co_queue_next(&bs->throttled_reqs[is_write]);
}
+size_t bdrv_opt_mem_align(BlockDriverState *bs)
+{
+ if (!bs || !bs->drv) {
+ /* 4k should be on the safe side */
+ return 4096;
+ }
+
+ return bs->bl.opt_mem_alignment;
+}
+
/* check if the path starts with "<protocol>:" */
static int path_has_protocol(const char *path)
{
@@ -327,7 +340,7 @@ BlockDriverState *bdrv_new(const char *device_name)
QLIST_INIT(&bs->dirty_bitmaps);
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
if (device_name[0] != '\0') {
- QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
+ QTAILQ_INSERT_TAIL(&bdrv_states, bs, device_list);
}
bdrv_iostatus_disable(bs);
notifier_list_init(&bs->close_notifiers);
@@ -479,6 +492,43 @@ int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
return ret;
}
+int bdrv_refresh_limits(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+
+ memset(&bs->bl, 0, sizeof(bs->bl));
+
+ if (!drv) {
+ return 0;
+ }
+
+ /* Take some limits from the children as a default */
+ if (bs->file) {
+ bdrv_refresh_limits(bs->file);
+ bs->bl.opt_transfer_length = bs->file->bl.opt_transfer_length;
+ bs->bl.opt_mem_alignment = bs->file->bl.opt_mem_alignment;
+ } else {
+ bs->bl.opt_mem_alignment = 512;
+ }
+
+ if (bs->backing_hd) {
+ bdrv_refresh_limits(bs->backing_hd);
+ bs->bl.opt_transfer_length =
+ MAX(bs->bl.opt_transfer_length,
+ bs->backing_hd->bl.opt_transfer_length);
+ bs->bl.opt_mem_alignment =
+ MAX(bs->bl.opt_mem_alignment,
+ bs->backing_hd->bl.opt_mem_alignment);
+ }
+
+ /* Then let the driver override it */
+ if (drv->bdrv_refresh_limits) {
+ return drv->bdrv_refresh_limits(bs);
+ }
+
+ return 0;
+}
+
/*
* Create a uniquely-named empty temporary file.
* Return 0 upon success, otherwise a negative errno value.
@@ -732,6 +782,33 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
return open_flags;
}
+static int bdrv_assign_node_name(BlockDriverState *bs,
+ const char *node_name,
+ Error **errp)
+{
+ if (!node_name) {
+ return 0;
+ }
+
+ /* empty string node name is invalid */
+ if (node_name[0] == '\0') {
+ error_setg(errp, "Empty node name");
+ return -EINVAL;
+ }
+
+ /* takes care of avoiding duplicates node names */
+ if (bdrv_find_node(node_name)) {
+ error_setg(errp, "Duplicate node name");
+ return -EINVAL;
+ }
+
+ /* copy node name into the bs and insert it into the graph list */
+ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
+ QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
+
+ return 0;
+}
+
/*
* Common part for opening disk images and files
*
@@ -742,6 +819,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
{
int ret, open_flags;
const char *filename;
+ const char *node_name = NULL;
Error *local_err = NULL;
assert(drv != NULL);
@@ -756,6 +834,13 @@ 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");
+ ret = bdrv_assign_node_name(bs, node_name, errp);
+ if (ret < 0) {
+ return ret;
+ }
+ 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. */
@@ -765,7 +850,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
}
bs->open_flags = flags;
- bs->buffer_alignment = 512;
+ bs->guest_block_size = 512;
+ bs->request_alignment = 512;
bs->zero_beyond_eof = true;
open_flags = bdrv_open_flags(bs, flags);
bs->read_only = !(open_flags & BDRV_O_RDWR);
@@ -833,6 +919,10 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
goto free_and_fail;
}
+ bdrv_refresh_limits(bs);
+ assert(bdrv_opt_mem_align(bs) != 0);
+ assert(bs->request_alignment != 0);
+
#ifndef _WIN32
if (bs->is_temporary) {
assert(bs->filename[0] != '\0');
@@ -858,9 +948,10 @@ free_and_fail:
* dictionary, it needs to use QINCREF() before calling bdrv_file_open.
*/
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
- QDict *options, int flags, Error **errp)
+ const char *reference, QDict *options, int flags,
+ Error **errp)
{
- BlockDriverState *bs;
+ BlockDriverState *bs = NULL;
BlockDriver *drv;
const char *drvname;
bool allow_protocol_prefix = false;
@@ -872,6 +963,24 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
options = qdict_new();
}
+ if (reference) {
+ if (filename || qdict_size(options)) {
+ error_setg(errp, "Cannot reference an existing block device with "
+ "additional options or a new filename");
+ return -EINVAL;
+ }
+ QDECREF(options);
+
+ bs = bdrv_find(reference);
+ if (!bs) {
+ error_setg(errp, "Cannot find block device '%s'", reference);
+ return -ENODEV;
+ }
+ bdrv_ref(bs);
+ *pbs = bs;
+ return 0;
+ }
+
bs = bdrv_new("");
bs->options = options;
options = qdict_clone_shallow(options);
@@ -929,14 +1038,19 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
goto fail;
}
- ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
+ if (!drv->bdrv_file_open) {
+ ret = bdrv_open(bs, filename, options, flags, drv, &local_err);
+ options = NULL;
+ } else {
+ ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
+ }
if (ret < 0) {
error_propagate(errp, local_err);
goto fail;
}
/* Check if any unknown options were used */
- if (qdict_size(options) != 0) {
+ if (options && (qdict_size(options) != 0)) {
const QDictEntry *entry = qdict_first(options);
error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
drv->format_name, entry->key);
@@ -1016,12 +1130,92 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
error_free(local_err);
return ret;
}
- pstrcpy(bs->backing_file, sizeof(bs->backing_file),
- bs->backing_hd->file->filename);
+
+ if (bs->backing_hd->file) {
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->backing_hd->file->filename);
+ }
+
+ /* Recalculate the BlockLimits with the backing file */
+ bdrv_refresh_limits(bs);
+
return 0;
}
/*
+ * Opens a disk image whose options are given as BlockdevRef in another block
+ * device's options.
+ *
+ * If force_raw is true, bdrv_file_open() will be used, thereby preventing any
+ * image format auto-detection. If it is false and a filename is given,
+ * bdrv_open() will be used for auto-detection.
+ *
+ * If allow_none is true, no image will be opened if filename is false and no
+ * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
+ *
+ * bdrev_key specifies the key for the image's BlockdevRef in the options QDict.
+ * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict
+ * itself, all options starting with "${bdref_key}." are considered part of the
+ * BlockdevRef.
+ *
+ * The BlockdevRef will be removed from the options QDict.
+ */
+int bdrv_open_image(BlockDriverState **pbs, const char *filename,
+ QDict *options, const char *bdref_key, int flags,
+ bool force_raw, bool allow_none, Error **errp)
+{
+ QDict *image_options;
+ int ret;
+ char *bdref_key_dot;
+ const char *reference;
+
+ bdref_key_dot = g_strdup_printf("%s.", bdref_key);
+ qdict_extract_subqdict(options, &image_options, bdref_key_dot);
+ g_free(bdref_key_dot);
+
+ reference = qdict_get_try_str(options, bdref_key);
+ if (!filename && !reference && !qdict_size(image_options)) {
+ if (allow_none) {
+ ret = 0;
+ } else {
+ error_setg(errp, "A block device must be specified for \"%s\"",
+ bdref_key);
+ ret = -EINVAL;
+ }
+ goto done;
+ }
+
+ if (filename && !force_raw) {
+ /* If a filename is given and the block driver should be detected
+ automatically (instead of using none), use bdrv_open() in order to do
+ that auto-detection. */
+ BlockDriverState *bs;
+
+ if (reference) {
+ error_setg(errp, "Cannot reference an existing block device while "
+ "giving a filename");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ bs = bdrv_new("");
+ ret = bdrv_open(bs, filename, image_options, flags, NULL, errp);
+ if (ret < 0) {
+ bdrv_unref(bs);
+ } else {
+ *pbs = bs;
+ }
+ } else {
+ ret = bdrv_file_open(pbs, filename, reference, image_options, flags,
+ errp);
+ }
+
+done:
+ qdict_del(options, bdref_key);
+ return ret;
+}
+
+/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*
* options is a QDict of options to pass to the block drivers, or NULL for an
@@ -1036,7 +1230,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
char tmp_filename[PATH_MAX + 1];
BlockDriverState *file = NULL;
- QDict *file_options = NULL;
const char *drvname;
Error *local_err = NULL;
@@ -1122,10 +1315,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
flags |= BDRV_O_ALLOW_RDWR;
}
- qdict_extract_subqdict(options, &file_options, "file.");
-
- ret = bdrv_file_open(&file, filename, file_options,
- bdrv_open_flags(bs, flags | BDRV_O_UNMAP), &local_err);
+ ret = bdrv_open_image(&file, filename, options, "file",
+ bdrv_open_flags(bs, flags | BDRV_O_UNMAP), true, true,
+ &local_err);
if (ret < 0) {
goto fail;
}
@@ -1143,7 +1335,13 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
}
if (!drv) {
- ret = find_image_format(file, filename, &drv, &local_err);
+ if (file) {
+ ret = find_image_format(file, filename, &drv, &local_err);
+ } else {
+ error_setg(errp, "Must specify either driver or file");
+ ret = -EINVAL;
+ goto unlink_and_fail;
+ }
}
if (!drv) {
@@ -1156,7 +1354,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
goto unlink_and_fail;
}
- if (bs->file != file) {
+ if (file && (bs->file != file)) {
bdrv_unref(file);
file = NULL;
}
@@ -1427,6 +1625,8 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
reopen_state->bs->enable_write_cache = !!(reopen_state->flags &
BDRV_O_CACHE_WB);
reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
+
+ bdrv_refresh_limits(reopen_state->bs);
}
/*
@@ -1501,7 +1701,7 @@ void bdrv_close_all(void)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
bdrv_close(bs);
}
}
@@ -1530,7 +1730,7 @@ static bool bdrv_requests_pending(BlockDriverState *bs)
static bool bdrv_requests_pending_all(void)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
if (bdrv_requests_pending(bs)) {
return true;
}
@@ -1557,7 +1757,7 @@ void bdrv_drain_all(void)
BlockDriverState *bs;
while (busy) {
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
bdrv_start_throttled_reqs(bs);
}
@@ -1566,14 +1766,19 @@ void bdrv_drain_all(void)
}
}
-/* make a BlockDriverState anonymous by removing from bdrv_state list.
+/* make a BlockDriverState anonymous by removing from bdrv_state and
+ * graph_bdrv_state list.
Also, NULL terminate the device_name to prevent double remove */
void bdrv_make_anon(BlockDriverState *bs)
{
if (bs->device_name[0] != '\0') {
- QTAILQ_REMOVE(&bdrv_states, bs, list);
+ QTAILQ_REMOVE(&bdrv_states, bs, device_list);
}
bs->device_name[0] = '\0';
+ if (bs->node_name[0] != '\0') {
+ QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list);
+ }
+ bs->node_name[0] = '\0';
}
static void bdrv_rebind(BlockDriverState *bs)
@@ -1593,7 +1798,7 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
bs_dest->dev_ops = bs_src->dev_ops;
bs_dest->dev_opaque = bs_src->dev_opaque;
bs_dest->dev = bs_src->dev;
- bs_dest->buffer_alignment = bs_src->buffer_alignment;
+ bs_dest->guest_block_size = bs_src->guest_block_size;
bs_dest->copy_on_read = bs_src->copy_on_read;
bs_dest->enable_write_cache = bs_src->enable_write_cache;
@@ -1627,7 +1832,12 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
/* keep the same entry in bdrv_states */
pstrcpy(bs_dest->device_name, sizeof(bs_dest->device_name),
bs_src->device_name);
- bs_dest->list = bs_src->list;
+ bs_dest->device_list = bs_src->device_list;
+
+ /* keep the same entry in graph_bdrv_states
+ * We do want to swap name but don't want to swap linked list entries
+ */
+ bs_dest->node_list = bs_src->node_list;
}
/*
@@ -1745,7 +1955,7 @@ void bdrv_detach_dev(BlockDriverState *bs, void *dev)
bs->dev = NULL;
bs->dev_ops = NULL;
bs->dev_opaque = NULL;
- bs->buffer_alignment = 512;
+ bs->guest_block_size = 512;
}
/* TODO change to return DeviceState * when all users are qdevified */
@@ -1876,10 +2086,10 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix)
int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
- int64_t sector, total_sectors;
+ int64_t sector, total_sectors, length, backing_length;
int n, ro, open_flags;
int ret = 0;
- uint8_t *buf;
+ uint8_t *buf = NULL;
char filename[PATH_MAX];
if (!drv)
@@ -1904,7 +2114,29 @@ int bdrv_commit(BlockDriverState *bs)
}
}
- total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
+ length = bdrv_getlength(bs);
+ if (length < 0) {
+ ret = length;
+ goto ro_cleanup;
+ }
+
+ backing_length = bdrv_getlength(bs->backing_hd);
+ if (backing_length < 0) {
+ ret = backing_length;
+ goto ro_cleanup;
+ }
+
+ /* If our top snapshot is larger than the backing file image,
+ * grow the backing file image if possible. If not possible,
+ * we must return an error */
+ if (length > backing_length) {
+ ret = bdrv_truncate(bs->backing_hd, length);
+ if (ret < 0) {
+ goto ro_cleanup;
+ }
+ }
+
+ total_sectors = length >> BDRV_SECTOR_BITS;
buf = g_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
for (sector = 0; sector < total_sectors; sector += n) {
@@ -1913,13 +2145,13 @@ int bdrv_commit(BlockDriverState *bs)
goto ro_cleanup;
}
if (ret) {
- if (bdrv_read(bs, sector, buf, n) != 0) {
- ret = -EIO;
+ ret = bdrv_read(bs, sector, buf, n);
+ if (ret < 0) {
goto ro_cleanup;
}
- if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
- ret = -EIO;
+ ret = bdrv_write(bs->backing_hd, sector, buf, n);
+ if (ret < 0) {
goto ro_cleanup;
}
}
@@ -1927,6 +2159,9 @@ int bdrv_commit(BlockDriverState *bs)
if (drv->bdrv_make_empty) {
ret = drv->bdrv_make_empty(bs);
+ if (ret < 0) {
+ goto ro_cleanup;
+ }
bdrv_flush(bs);
}
@@ -1934,9 +2169,11 @@ int bdrv_commit(BlockDriverState *bs)
* Make sure all data we wrote to the backing device is actually
* stable on disk.
*/
- if (bs->backing_hd)
+ if (bs->backing_hd) {
bdrv_flush(bs->backing_hd);
+ }
+ ret = 0;
ro_cleanup:
g_free(buf);
@@ -1952,7 +2189,7 @@ int bdrv_commit_all(void)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
if (bs->drv && bs->backing_hd) {
int ret = bdrv_commit(bs);
if (ret < 0) {
@@ -1970,6 +2207,10 @@ int bdrv_commit_all(void)
*/
static void tracked_request_end(BdrvTrackedRequest *req)
{
+ if (req->serialising) {
+ req->bs->serialising_in_flight--;
+ }
+
QLIST_REMOVE(req, list);
qemu_co_queue_restart_all(&req->wait_queue);
}
@@ -1979,15 +2220,18 @@ static void tracked_request_end(BdrvTrackedRequest *req)
*/
static void tracked_request_begin(BdrvTrackedRequest *req,
BlockDriverState *bs,
- int64_t sector_num,
- int nb_sectors, bool is_write)
+ int64_t offset,
+ unsigned int bytes, bool is_write)
{
*req = (BdrvTrackedRequest){
.bs = bs,
- .sector_num = sector_num,
- .nb_sectors = nb_sectors,
- .is_write = is_write,
- .co = qemu_coroutine_self(),
+ .offset = offset,
+ .bytes = bytes,
+ .is_write = is_write,
+ .co = qemu_coroutine_self(),
+ .serialising = false,
+ .overlap_offset = offset,
+ .overlap_bytes = bytes,
};
qemu_co_queue_init(&req->wait_queue);
@@ -1995,6 +2239,21 @@ static void tracked_request_begin(BdrvTrackedRequest *req,
QLIST_INSERT_HEAD(&bs->tracked_requests, req, list);
}
+static void mark_request_serialising(BdrvTrackedRequest *req, size_t align)
+{
+ int64_t overlap_offset = req->offset & ~(align - 1);
+ int overlap_bytes = ROUND_UP(req->offset + req->bytes, align)
+ - overlap_offset;
+
+ if (!req->serialising) {
+ req->bs->serialising_in_flight++;
+ req->serialising = true;
+ }
+
+ req->overlap_offset = MIN(req->overlap_offset, overlap_offset);
+ req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
+}
+
/**
* Round a region to cluster boundaries
*/
@@ -2016,53 +2275,75 @@ void bdrv_round_to_clusters(BlockDriverState *bs,
}
}
+static int bdrv_get_cluster_size(BlockDriverState *bs)
+{
+ BlockDriverInfo bdi;
+ int ret;
+
+ ret = bdrv_get_info(bs, &bdi);
+ if (ret < 0 || bdi.cluster_size == 0) {
+ return bs->request_alignment;
+ } else {
+ return bdi.cluster_size;
+ }
+}
+
static bool tracked_request_overlaps(BdrvTrackedRequest *req,
- int64_t sector_num, int nb_sectors) {
+ int64_t offset, unsigned int bytes)
+{
/* aaaa bbbb */
- if (sector_num >= req->sector_num + req->nb_sectors) {
+ if (offset >= req->overlap_offset + req->overlap_bytes) {
return false;
}
/* bbbb aaaa */
- if (req->sector_num >= sector_num + nb_sectors) {
+ if (req->overlap_offset >= offset + bytes) {
return false;
}
return true;
}
-static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors)
+static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
{
+ BlockDriverState *bs = self->bs;
BdrvTrackedRequest *req;
- int64_t cluster_sector_num;
- int cluster_nb_sectors;
bool retry;
+ bool waited = false;
- /* If we touch the same cluster it counts as an overlap. This guarantees
- * that allocating writes will be serialized and not race with each other
- * for the same cluster. For example, in copy-on-read it ensures that the
- * CoR read and write operations are atomic and guest writes cannot
- * interleave between them.
- */
- bdrv_round_to_clusters(bs, sector_num, nb_sectors,
- &cluster_sector_num, &cluster_nb_sectors);
+ if (!bs->serialising_in_flight) {
+ return false;
+ }
do {
retry = false;
QLIST_FOREACH(req, &bs->tracked_requests, list) {
- if (tracked_request_overlaps(req, cluster_sector_num,
- cluster_nb_sectors)) {
+ if (req == self || (!req->serialising && !self->serialising)) {
+ continue;
+ }
+ if (tracked_request_overlaps(req, self->overlap_offset,
+ self->overlap_bytes))
+ {
/* Hitting this means there was a reentrant request, for
* example, a block driver issuing nested requests. This must
* never happen since it means deadlock.
*/
assert(qemu_coroutine_self() != req->co);
- qemu_co_queue_wait(&req->wait_queue);
- retry = true;
- break;
+ /* If the request is already (indirectly) waiting for us, or
+ * will wait for us as soon as it wakes up, then just go on
+ * (instead of producing a deadlock in the former case). */
+ if (!req->waiting_for) {
+ self->waiting_for = req;
+ qemu_co_queue_wait(&req->wait_queue);
+ self->waiting_for = NULL;
+ retry = true;
+ waited = true;
+ break;
+ }
}
}
} while (retry);
+
+ return waited;
}
/*
@@ -2224,6 +2505,7 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
}
new_top_bs->backing_hd = base_bs;
+ bdrv_refresh_limits(new_top_bs);
QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) {
/* so that bdrv_close() does not recursively close the chain */
@@ -2271,8 +2553,7 @@ static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
typedef struct RwCo {
BlockDriverState *bs;
- int64_t sector_num;
- int nb_sectors;
+ int64_t offset;
QEMUIOVector *qiov;
bool is_write;
int ret;
@@ -2284,34 +2565,32 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
RwCo *rwco = opaque;
if (!rwco->is_write) {
- rwco->ret = bdrv_co_do_readv(rwco->bs, rwco->sector_num,
- rwco->nb_sectors, rwco->qiov,
- rwco->flags);
- } else {
- rwco->ret = bdrv_co_do_writev(rwco->bs, rwco->sector_num,
- rwco->nb_sectors, rwco->qiov,
+ rwco->ret = bdrv_co_do_preadv(rwco->bs, rwco->offset,
+ rwco->qiov->size, rwco->qiov,
rwco->flags);
+ } else {
+ rwco->ret = bdrv_co_do_pwritev(rwco->bs, rwco->offset,
+ rwco->qiov->size, rwco->qiov,
+ rwco->flags);
}
}
/*
* Process a vectored synchronous request using coroutines
*/
-static int bdrv_rwv_co(BlockDriverState *bs, int64_t sector_num,
- QEMUIOVector *qiov, bool is_write,
- BdrvRequestFlags flags)
+static int bdrv_prwv_co(BlockDriverState *bs, int64_t offset,
+ QEMUIOVector *qiov, bool is_write,
+ BdrvRequestFlags flags)
{
Coroutine *co;
RwCo rwco = {
.bs = bs,
- .sector_num = sector_num,
- .nb_sectors = qiov->size >> BDRV_SECTOR_BITS,
+ .offset = offset,
.qiov = qiov,
.is_write = is_write,
.ret = NOT_DONE,
.flags = flags,
};
- assert((qiov->size & (BDRV_SECTOR_SIZE - 1)) == 0);
/**
* In sync call context, when the vcpu is blocked, this throttling timer
@@ -2350,7 +2629,8 @@ static int bdrv_rw_co(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
};
qemu_iovec_init_external(&qiov, &iov, 1);
- return bdrv_rwv_co(bs, sector_num, &qiov, is_write, flags);
+ return bdrv_prwv_co(bs, sector_num << BDRV_SECTOR_BITS,
+ &qiov, is_write, flags);
}
/* return < 0 if error. See bdrv_write() for the return codes */
@@ -2386,11 +2666,6 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
return bdrv_rw_co(bs, sector_num, (uint8_t *)buf, nb_sectors, true, 0);
}
-int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov)
-{
- return bdrv_rwv_co(bs, sector_num, qiov, true, 0);
-}
-
int bdrv_write_zeroes(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, BdrvRequestFlags flags)
{
@@ -2421,6 +2696,11 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
nb_sectors = INT_MAX;
}
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &n);
+ if (ret < 0) {
+ error_report("error getting block status at sector %" PRId64 ": %s",
+ sector_num, strerror(-ret));
+ return ret;
+ }
if (ret & BDRV_BLOCK_ZERO) {
sector_num += n;
continue;
@@ -2435,117 +2715,53 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
}
}
-int bdrv_pread(BlockDriverState *bs, int64_t offset,
- void *buf, int count1)
+int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes)
{
- uint8_t tmp_buf[BDRV_SECTOR_SIZE];
- int len, nb_sectors, count;
- int64_t sector_num;
+ QEMUIOVector qiov;
+ struct iovec iov = {
+ .iov_base = (void *)buf,
+ .iov_len = bytes,
+ };
int ret;
- count = count1;
- /* first read to align to sector start */
- len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
- if (len > count)
- len = count;
- sector_num = offset >> BDRV_SECTOR_BITS;
- if (len > 0) {
- if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- memcpy(buf, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), len);
- count -= len;
- if (count == 0)
- return count1;
- sector_num++;
- buf += len;
- }
-
- /* read the sectors "in place" */
- nb_sectors = count >> BDRV_SECTOR_BITS;
- if (nb_sectors > 0) {
- if ((ret = bdrv_read(bs, sector_num, buf, nb_sectors)) < 0)
- return ret;
- sector_num += nb_sectors;
- len = nb_sectors << BDRV_SECTOR_BITS;
- buf += len;
- count -= len;
+ if (bytes < 0) {
+ return -EINVAL;
}
- /* add data from the last sector */
- if (count > 0) {
- if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- memcpy(buf, tmp_buf, count);
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ ret = bdrv_prwv_co(bs, offset, &qiov, false, 0);
+ if (ret < 0) {
+ return ret;
}
- return count1;
+
+ return bytes;
}
int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov)
{
- uint8_t tmp_buf[BDRV_SECTOR_SIZE];
- int len, nb_sectors, count;
- int64_t sector_num;
int ret;
- count = qiov->size;
-
- /* first write to align to sector start */
- len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
- if (len > count)
- len = count;
- sector_num = offset >> BDRV_SECTOR_BITS;
- if (len > 0) {
- if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- qemu_iovec_to_buf(qiov, 0, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)),
- len);
- if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- count -= len;
- if (count == 0)
- return qiov->size;
- sector_num++;
- }
-
- /* write the sectors "in place" */
- nb_sectors = count >> BDRV_SECTOR_BITS;
- if (nb_sectors > 0) {
- QEMUIOVector qiov_inplace;
-
- qemu_iovec_init(&qiov_inplace, qiov->niov);
- qemu_iovec_concat(&qiov_inplace, qiov, len,
- nb_sectors << BDRV_SECTOR_BITS);
- ret = bdrv_writev(bs, sector_num, &qiov_inplace);
- qemu_iovec_destroy(&qiov_inplace);
- if (ret < 0) {
- return ret;
- }
-
- sector_num += nb_sectors;
- len = nb_sectors << BDRV_SECTOR_BITS;
- count -= len;
+ ret = bdrv_prwv_co(bs, offset, qiov, true, 0);
+ if (ret < 0) {
+ return ret;
}
- /* add data from the last sector */
- if (count > 0) {
- if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- qemu_iovec_to_buf(qiov, qiov->size - count, tmp_buf, count);
- if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
- return ret;
- }
return qiov->size;
}
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
- const void *buf, int count1)
+ const void *buf, int bytes)
{
QEMUIOVector qiov;
struct iovec iov = {
.iov_base = (void *) buf,
- .iov_len = count1,
+ .iov_len = bytes,
};
+ if (bytes < 0) {
+ return -EINVAL;
+ }
+
qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_pwritev(bs, offset, &qiov);
}
@@ -2641,40 +2857,34 @@ err:
}
/*
- * Handle a read request in coroutine context
+ * Forwards an already correctly aligned request to the BlockDriver. This
+ * handles copy on read and zeroing after EOF; any other features must be
+ * implemented by the caller.
*/
-static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
- BdrvRequestFlags flags)
+static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
+ BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
+ int64_t align, QEMUIOVector *qiov, int flags)
{
BlockDriver *drv = bs->drv;
- BdrvTrackedRequest req;
int ret;
- if (!drv) {
- return -ENOMEDIUM;
- }
- if (bdrv_check_request(bs, sector_num, nb_sectors)) {
- return -EIO;
- }
+ int64_t sector_num = offset >> BDRV_SECTOR_BITS;
+ unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS;
- if (bs->copy_on_read) {
- flags |= BDRV_REQ_COPY_ON_READ;
- }
- if (flags & BDRV_REQ_COPY_ON_READ) {
- bs->copy_on_read_in_flight++;
- }
-
- if (bs->copy_on_read_in_flight) {
- wait_for_overlapping_requests(bs, sector_num, nb_sectors);
- }
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
- /* throttling disk I/O */
- if (bs->io_limits_enabled) {
- bdrv_io_limits_intercept(bs, nb_sectors, false);
+ /* Handle Copy on Read and associated serialisation */
+ if (flags & BDRV_REQ_COPY_ON_READ) {
+ /* If we touch the same cluster it counts as an overlap. This
+ * guarantees that allocating writes will be serialized and not race
+ * with each other for the same cluster. For example, in copy-on-read
+ * it ensures that the CoR read and write operations are atomic and
+ * guest writes cannot interleave between them. */
+ mark_request_serialising(req, bdrv_get_cluster_size(bs));
}
- tracked_request_begin(&req, bs, sector_num, nb_sectors, false);
+ wait_serialising_requests(req);
if (flags & BDRV_REQ_COPY_ON_READ) {
int pnum;
@@ -2690,6 +2900,7 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
}
}
+ /* Forward the request to the BlockDriver */
if (!(bs->zero_beyond_eof && bs->growable)) {
ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov);
} else {
@@ -2703,7 +2914,8 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
}
total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE);
- max_nb_sectors = MAX(0, total_sectors - sector_num);
+ max_nb_sectors = MAX(0, ROUND_UP(total_sectors - sector_num,
+ align >> BDRV_SECTOR_BITS));
if (max_nb_sectors > 0) {
ret = drv->bdrv_co_readv(bs, sector_num,
MIN(nb_sectors, max_nb_sectors), qiov);
@@ -2721,15 +2933,95 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
}
out:
+ return ret;
+}
+
+/*
+ * Handle a read request in coroutine context
+ */
+static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
+ int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
+{
+ BlockDriver *drv = bs->drv;
+ BdrvTrackedRequest req;
+
+ /* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
+ uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
+ uint8_t *head_buf = NULL;
+ uint8_t *tail_buf = NULL;
+ QEMUIOVector local_qiov;
+ bool use_local_qiov = false;
+ int ret;
+
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (bdrv_check_byte_request(bs, offset, bytes)) {
+ return -EIO;
+ }
+
+ if (bs->copy_on_read) {
+ flags |= BDRV_REQ_COPY_ON_READ;
+ }
+
+ /* throttling disk I/O */
+ if (bs->io_limits_enabled) {
+ bdrv_io_limits_intercept(bs, bytes, false);
+ }
+
+ /* Align read if necessary by padding qiov */
+ if (offset & (align - 1)) {
+ head_buf = qemu_blockalign(bs, align);
+ qemu_iovec_init(&local_qiov, qiov->niov + 2);
+ qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
+ qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
+ use_local_qiov = true;
+
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
+ }
+
+ if ((offset + bytes) & (align - 1)) {
+ if (!use_local_qiov) {
+ qemu_iovec_init(&local_qiov, qiov->niov + 1);
+ qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
+ use_local_qiov = true;
+ }
+ tail_buf = qemu_blockalign(bs, align);
+ qemu_iovec_add(&local_qiov, tail_buf,
+ align - ((offset + bytes) & (align - 1)));
+
+ bytes = ROUND_UP(bytes, align);
+ }
+
+ tracked_request_begin(&req, bs, offset, bytes, false);
+ ret = bdrv_aligned_preadv(bs, &req, offset, bytes, align,
+ use_local_qiov ? &local_qiov : qiov,
+ flags);
tracked_request_end(&req);
- if (flags & BDRV_REQ_COPY_ON_READ) {
- bs->copy_on_read_in_flight--;
+ if (use_local_qiov) {
+ qemu_iovec_destroy(&local_qiov);
+ qemu_vfree(head_buf);
+ qemu_vfree(tail_buf);
}
return ret;
}
+static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
+{
+ if (nb_sectors < 0 || nb_sectors > (UINT_MAX >> BDRV_SECTOR_BITS)) {
+ return -EINVAL;
+ }
+
+ return bdrv_co_do_preadv(bs, sector_num << BDRV_SECTOR_BITS,
+ nb_sectors << BDRV_SECTOR_BITS, qiov, flags);
+}
+
int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
@@ -2823,46 +3115,37 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
}
/*
- * Handle a write request in coroutine context
+ * Forwards an already correctly aligned write request to the BlockDriver.
*/
-static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
- BdrvRequestFlags flags)
+static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
+ BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
+ QEMUIOVector *qiov, int flags)
{
BlockDriver *drv = bs->drv;
- BdrvTrackedRequest req;
+ bool waited;
int ret;
- if (!bs->drv) {
- return -ENOMEDIUM;
- }
- if (bs->read_only) {
- return -EACCES;
- }
- if (bdrv_check_request(bs, sector_num, nb_sectors)) {
- return -EIO;
- }
-
- if (bs->copy_on_read_in_flight) {
- wait_for_overlapping_requests(bs, sector_num, nb_sectors);
- }
+ int64_t sector_num = offset >> BDRV_SECTOR_BITS;
+ unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS;
- /* throttling disk I/O */
- if (bs->io_limits_enabled) {
- bdrv_io_limits_intercept(bs, nb_sectors, true);
- }
+ assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
- tracked_request_begin(&req, bs, sector_num, nb_sectors, true);
+ waited = wait_serialising_requests(req);
+ assert(!waited || !req->serialising);
- ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
+ ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);
if (ret < 0) {
/* Do nothing, write notifier decided to fail this request */
} else if (flags & BDRV_REQ_ZERO_WRITE) {
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_ZERO);
ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors, flags);
} else {
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV);
ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
}
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_DONE);
if (ret == 0 && !bs->enable_write_cache) {
ret = bdrv_co_flush(bs);
@@ -2877,11 +3160,143 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors);
}
+ return ret;
+}
+
+/*
+ * Handle a write request in coroutine context
+ */
+static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
+ int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
+{
+ BdrvTrackedRequest req;
+ /* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */
+ uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
+ uint8_t *head_buf = NULL;
+ uint8_t *tail_buf = NULL;
+ QEMUIOVector local_qiov;
+ bool use_local_qiov = false;
+ int ret;
+
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (bs->read_only) {
+ return -EACCES;
+ }
+ if (bdrv_check_byte_request(bs, offset, bytes)) {
+ return -EIO;
+ }
+
+ /* throttling disk I/O */
+ if (bs->io_limits_enabled) {
+ bdrv_io_limits_intercept(bs, bytes, true);
+ }
+
+ /*
+ * Align write if necessary by performing a read-modify-write cycle.
+ * Pad qiov with the read parts and be sure to have a tracked request not
+ * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle.
+ */
+ tracked_request_begin(&req, bs, offset, bytes, true);
+
+ if (offset & (align - 1)) {
+ QEMUIOVector head_qiov;
+ struct iovec head_iov;
+
+ mark_request_serialising(&req, align);
+ wait_serialising_requests(&req);
+
+ head_buf = qemu_blockalign(bs, align);
+ head_iov = (struct iovec) {
+ .iov_base = head_buf,
+ .iov_len = align,
+ };
+ qemu_iovec_init_external(&head_qiov, &head_iov, 1);
+
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_HEAD);
+ ret = bdrv_aligned_preadv(bs, &req, offset & ~(align - 1), align,
+ align, &head_qiov, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
+
+ qemu_iovec_init(&local_qiov, qiov->niov + 2);
+ qemu_iovec_add(&local_qiov, head_buf, offset & (align - 1));
+ qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
+ use_local_qiov = true;
+
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
+ }
+
+ if ((offset + bytes) & (align - 1)) {
+ QEMUIOVector tail_qiov;
+ struct iovec tail_iov;
+ size_t tail_bytes;
+ bool waited;
+
+ mark_request_serialising(&req, align);
+ waited = wait_serialising_requests(&req);
+ assert(!waited || !use_local_qiov);
+
+ tail_buf = qemu_blockalign(bs, align);
+ tail_iov = (struct iovec) {
+ .iov_base = tail_buf,
+ .iov_len = align,
+ };
+ qemu_iovec_init_external(&tail_qiov, &tail_iov, 1);
+
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_TAIL);
+ ret = bdrv_aligned_preadv(bs, &req, (offset + bytes) & ~(align - 1), align,
+ align, &tail_qiov, 0);
+ if (ret < 0) {
+ goto fail;
+ }
+ BLKDBG_EVENT(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
+
+ if (!use_local_qiov) {
+ qemu_iovec_init(&local_qiov, qiov->niov + 1);
+ qemu_iovec_concat(&local_qiov, qiov, 0, qiov->size);
+ use_local_qiov = true;
+ }
+
+ tail_bytes = (offset + bytes) & (align - 1);
+ qemu_iovec_add(&local_qiov, tail_buf + tail_bytes, align - tail_bytes);
+
+ bytes = ROUND_UP(bytes, align);
+ }
+
+ ret = bdrv_aligned_pwritev(bs, &req, offset, bytes,
+ use_local_qiov ? &local_qiov : qiov,
+ flags);
+
+fail:
tracked_request_end(&req);
+ if (use_local_qiov) {
+ qemu_iovec_destroy(&local_qiov);
+ qemu_vfree(head_buf);
+ qemu_vfree(tail_buf);
+ }
+
return ret;
}
+static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
+{
+ if (nb_sectors < 0 || nb_sectors > (INT_MAX >> BDRV_SECTOR_BITS)) {
+ return -EINVAL;
+ }
+
+ return bdrv_co_do_pwritev(bs, sector_num << BDRV_SECTOR_BITS,
+ nb_sectors << BDRV_SECTOR_BITS, qiov, flags);
+}
+
int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
@@ -3105,11 +3520,12 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
}
}
+/* This function is to find block backend bs */
BlockDriverState *bdrv_find(const char *name)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
if (!strcmp(name, bs->device_name)) {
return bs;
}
@@ -3117,19 +3533,83 @@ BlockDriverState *bdrv_find(const char *name)
return NULL;
}
+/* This function is to find a node in the bs graph */
+BlockDriverState *bdrv_find_node(const char *node_name)
+{
+ BlockDriverState *bs;
+
+ assert(node_name);
+
+ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) {
+ if (!strcmp(node_name, bs->node_name)) {
+ return bs;
+ }
+ }
+ return NULL;
+}
+
+/* Put this QMP function here so it can access the static graph_bdrv_states. */
+BlockDeviceInfoList *bdrv_named_nodes_list(void)
+{
+ BlockDeviceInfoList *list, *entry;
+ BlockDriverState *bs;
+
+ list = NULL;
+ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) {
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = bdrv_block_device_info(bs);
+ entry->next = list;
+ list = entry;
+ }
+
+ return list;
+}
+
+BlockDriverState *bdrv_lookup_bs(const char *device,
+ const char *node_name,
+ Error **errp)
+{
+ BlockDriverState *bs = NULL;
+
+ if ((!device && !node_name) || (device && node_name)) {
+ error_setg(errp, "Use either device or node-name but not both");
+ return NULL;
+ }
+
+ if (device) {
+ bs = bdrv_find(device);
+
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return NULL;
+ }
+
+ return bs;
+ }
+
+ bs = bdrv_find_node(node_name);
+
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, node_name);
+ return NULL;
+ }
+
+ return bs;
+}
+
BlockDriverState *bdrv_next(BlockDriverState *bs)
{
if (!bs) {
return QTAILQ_FIRST(&bdrv_states);
}
- return QTAILQ_NEXT(bs, list);
+ return QTAILQ_NEXT(bs, device_list);
}
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
it(opaque, bs);
}
}
@@ -3149,7 +3629,7 @@ int bdrv_flush_all(void)
BlockDriverState *bs;
int result = 0;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
int ret = bdrv_flush(bs);
if (ret < 0 && !result) {
result = ret;
@@ -4273,7 +4753,7 @@ void bdrv_invalidate_cache_all(void)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
bdrv_invalidate_cache(bs);
}
}
@@ -4282,7 +4762,7 @@ void bdrv_clear_incoming_migration_all(void)
{
BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
bs->open_flags = bs->open_flags & ~(BDRV_O_INCOMING);
}
}
@@ -4309,9 +4789,15 @@ int bdrv_flush(BlockDriverState *bs)
return rwco.ret;
}
+typedef struct DiscardCo {
+ BlockDriverState *bs;
+ int64_t sector_num;
+ int nb_sectors;
+ int ret;
+} DiscardCo;
static void coroutine_fn bdrv_discard_co_entry(void *opaque)
{
- RwCo *rwco = opaque;
+ DiscardCo *rwco = opaque;
rwco->ret = bdrv_co_discard(rwco->bs, rwco->sector_num, rwco->nb_sectors);
}
@@ -4395,7 +4881,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
{
Coroutine *co;
- RwCo rwco = {
+ DiscardCo rwco = {
.bs = bs,
.sector_num = sector_num,
.nb_sectors = nb_sectors,
@@ -4500,14 +4986,14 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
return NULL;
}
-void bdrv_set_buffer_alignment(BlockDriverState *bs, int align)
+void bdrv_set_guest_block_size(BlockDriverState *bs, int align)
{
- bs->buffer_alignment = align;
+ bs->guest_block_size = align;
}
void *qemu_blockalign(BlockDriverState *bs, size_t size)
{
- return qemu_memalign((bs && bs->buffer_alignment) ? bs->buffer_alignment : 512, size);
+ return qemu_memalign(bdrv_opt_mem_align(bs), size);
}
/*
@@ -4516,9 +5002,13 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size)
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
{
int i;
+ size_t alignment = bdrv_opt_mem_align(bs);
for (i = 0; i < qiov->niov; i++) {
- if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
+ if ((uintptr_t) qiov->iov[i].iov_base % alignment) {
+ return false;
+ }
+ if (qiov->iov[i].iov_len % alignment) {
return false;
}
}
@@ -4870,21 +5360,68 @@ int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
return bs->drv->bdrv_amend_options(bs, options);
}
-ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs)
+/* Used to recurse on single child block filters.
+ * Single child block filter will store their child in bs->file.
+ */
+bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
+ BlockDriverState *candidate)
{
- if (bs->drv->bdrv_check_ext_snapshot) {
- return bs->drv->bdrv_check_ext_snapshot(bs);
+ if (!bs->drv) {
+ return false;
+ }
+
+ if (!bs->drv->authorizations[BS_IS_A_FILTER]) {
+ if (bs == candidate) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!bs->drv->authorizations[BS_FILTER_PASS_DOWN]) {
+ return false;
}
- if (bs->file && bs->file->drv && bs->file->drv->bdrv_check_ext_snapshot) {
- return bs->file->drv->bdrv_check_ext_snapshot(bs);
+ if (!bs->file) {
+ return false;
+ }
+
+ return bdrv_recurse_is_first_non_filter(bs->file, candidate);
+}
+
+bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
+ BlockDriverState *candidate)
+{
+ if (bs->drv && bs->drv->bdrv_recurse_is_first_non_filter) {
+ return bs->drv->bdrv_recurse_is_first_non_filter(bs, candidate);
}
- /* external snapshots are allowed by default */
- return EXT_SNAPSHOT_ALLOWED;
+ return bdrv_generic_is_first_non_filter(bs, candidate);
}
-ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs)
+/* This function checks if the candidate is the first non filter bs down it's
+ * bs chain. Since we don't have pointers to parents it explore all bs chains
+ * from the top. Some filters can choose not to pass down the recursion.
+ */
+bool bdrv_is_first_non_filter(BlockDriverState *candidate)
{
- return EXT_SNAPSHOT_FORBIDDEN;
+ BlockDriverState *bs;
+
+ /* walk down the bs forest recursively */
+ QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ bool perm;
+
+ if (!bs->file) {
+ continue;
+ }
+
+ perm = bdrv_recurse_is_first_non_filter(bs->file, candidate);
+
+ /* candidate is the first non filter */
+ if (perm) {
+ return true;
+ }
+ }
+
+ return false;
}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index f43ecbc044..4e8c91ec34 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -10,7 +10,7 @@ block-obj-$(CONFIG_POSIX) += raw-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
ifeq ($(CONFIG_POSIX),y)
-block-obj-y += nbd.o sheepdog.o
+block-obj-y += nbd.o nbd-client.o sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
diff --git a/block/backup.c b/block/backup.c
index 0198514043..15a2e55e8e 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -181,8 +181,13 @@ static int coroutine_fn backup_before_write_notify(
void *opaque)
{
BdrvTrackedRequest *req = opaque;
+ int64_t sector_num = req->offset >> BDRV_SECTOR_BITS;
+ int nb_sectors = req->bytes >> BDRV_SECTOR_BITS;
- return backup_do_cow(req->bs, req->sector_num, req->nb_sectors, NULL);
+ assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0);
+ assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
+
+ return backup_do_cow(req->bs, sector_num, nb_sectors, NULL);
}
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 37cf028545..56c4cd084f 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -186,6 +186,14 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
+
+ [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head",
+ [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head",
+ [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail",
+ [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail",
+ [BLKDBG_PWRITEV] = "pwritev",
+ [BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
+ [BLKDBG_PWRITEV_DONE] = "pwritev_done",
};
static int get_event_by_name(const char *name, BlkDebugEvent *event)
@@ -271,19 +279,33 @@ static void remove_rule(BlkdebugRule *rule)
g_free(rule);
}
-static int read_config(BDRVBlkdebugState *s, const char *filename)
+static int read_config(BDRVBlkdebugState *s, const char *filename,
+ QDict *options, Error **errp)
{
- FILE *f;
+ FILE *f = NULL;
int ret;
struct add_rule_data d;
+ Error *local_err = NULL;
+
+ if (filename) {
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ error_setg_errno(errp, errno, "Could not read blkdebug config file");
+ return -errno;
+ }
- f = fopen(filename, "r");
- if (f == NULL) {
- return -errno;
+ ret = qemu_config_parse(f, config_groups, filename);
+ if (ret < 0) {
+ error_setg(errp, "Could not parse blkdebug config file");
+ ret = -EINVAL;
+ goto fail;
+ }
}
- ret = qemu_config_parse(f, config_groups, filename);
- if (ret < 0) {
+ qemu_config_parse_qdict(options, config_groups, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
goto fail;
}
@@ -298,7 +320,9 @@ static int read_config(BDRVBlkdebugState *s, const char *filename)
fail:
qemu_opts_reset(&inject_error_opts);
qemu_opts_reset(&set_state_opts);
- fclose(f);
+ if (f) {
+ fclose(f);
+ }
return ret;
}
@@ -310,7 +334,9 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
/* Parse the blkdebug: prefix */
if (!strstart(filename, "blkdebug:", &filename)) {
- error_setg(errp, "File name string must start with 'blkdebug:'");
+ /* There was no prefix; therefore, all options have to be already
+ present in the QDict (except for the filename) */
+ qdict_put(options, "x-image", qstring_from_str(filename));
return;
}
@@ -346,6 +372,11 @@ static QemuOptsList runtime_opts = {
.type = QEMU_OPT_STRING,
.help = "[internal use only, will be removed]",
},
+ {
+ .name = "align",
+ .type = QEMU_OPT_SIZE,
+ .help = "Required alignment in bytes",
+ },
{ /* end of list */ }
},
};
@@ -356,10 +387,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
BDRVBlkdebugState *s = bs->opaque;
QemuOpts *opts;
Error *local_err = NULL;
- const char *filename, *config;
+ const char *config;
+ uint64_t align;
int ret;
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -367,30 +399,31 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- /* Read rules from config file */
+ /* Read rules from config file or command line options */
config = qemu_opt_get(opts, "config");
- if (config) {
- ret = read_config(s, config);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not read blkdebug config file");
- goto fail;
- }
+ ret = read_config(s, config, options, errp);
+ if (ret) {
+ goto fail;
}
/* Set initial state */
s->state = 1;
/* Open the backing file */
- filename = qemu_opt_get(opts, "x-image");
- if (filename == NULL) {
- error_setg(errp, "Could not retrieve image file name");
- ret = -EINVAL;
+ ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
+ flags, true, false, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
goto fail;
}
- ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
+ /* Set request alignment */
+ align = qemu_opt_get_size(opts, "align", bs->request_alignment);
+ if (align > 0 && align < INT_MAX && !(align & (align - 1))) {
+ bs->request_alignment = align;
+ } else {
+ error_setg(errp, "Invalid alignment");
+ ret = -EINVAL;
goto fail;
}
@@ -594,9 +627,9 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
{
BDRVBlkdebugState *s = bs->opaque;
- BlkdebugSuspendedReq *r;
+ BlkdebugSuspendedReq *r, *next;
- QLIST_FOREACH(r, &s->suspended_reqs, next) {
+ QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
if (!strcmp(r->tag, tag)) {
qemu_coroutine_enter(r->co, NULL);
return 0;
@@ -609,7 +642,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
const char *tag)
{
BDRVBlkdebugState *s = bs->opaque;
- BlkdebugSuspendedReq *r;
+ BlkdebugSuspendedReq *r, *r_next;
BlkdebugRule *rule, *next;
int i, ret = -ENOENT;
@@ -622,7 +655,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
}
}
}
- QLIST_FOREACH(r, &s->suspended_reqs, next) {
+ QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
if (!strcmp(r->tag, tag)) {
qemu_coroutine_enter(r->co, NULL);
ret = 0;
diff --git a/block/blkverify.c b/block/blkverify.c
index 3c6352898f..cfcbcf41c3 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -78,7 +78,9 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
/* Parse the blkverify: prefix */
if (!strstart(filename, "blkverify:", &filename)) {
- error_setg(errp, "File name string must start with 'blkverify:'");
+ /* There was no prefix; therefore, all options have to be already
+ present in the QDict (except for the filename) */
+ qdict_put(options, "x-image", qstring_from_str(filename));
return;
}
@@ -122,10 +124,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
BDRVBlkverifyState *s = bs->opaque;
QemuOpts *opts;
Error *local_err = NULL;
- const char *filename, *raw;
int ret;
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -133,33 +134,19 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- /* Parse the raw image filename */
- raw = qemu_opt_get(opts, "x-raw");
- if (raw == NULL) {
- error_setg(errp, "Could not retrieve raw image filename");
- ret = -EINVAL;
- goto fail;
- }
-
- ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
+ /* Open the raw file */
+ ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
+ "raw", flags, true, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto fail;
}
/* Open the test file */
- filename = qemu_opt_get(opts, "x-image");
- if (filename == NULL) {
- error_setg(errp, "Could not retrieve test image filename");
- ret = -EINVAL;
- goto fail;
- }
-
- s->test_file = bdrv_new("");
- ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
+ ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
+ "test", flags, false, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
- bdrv_unref(s->test_file);
s->test_file = NULL;
goto fail;
}
@@ -417,7 +404,7 @@ static BlockDriver bdrv_blkverify = {
.bdrv_aio_writev = blkverify_aio_writev,
.bdrv_aio_flush = blkverify_aio_flush,
- .bdrv_check_ext_snapshot = bdrv_check_ext_snapshot_forbidden,
+ .authorizations = { true, false },
};
static void bdrv_blkverify_init(void)
diff --git a/block/commit.c b/block/commit.c
index d4090cbf7d..acec4ac5a8 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -198,13 +198,7 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
return;
}
- /* Once we support top == active layer, remove this check */
- if (top == bs) {
- error_setg(errp,
- "Top image as the active layer is currently unsupported");
- return;
- }
-
+ assert(top != bs);
if (top == base) {
error_setg(errp, "Invalid files for merge: top and base are the same");
return;
diff --git a/block/cow.c b/block/cow.c
index dc15e46b6c..7fc0b12163 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -351,7 +351,8 @@ static int cow_create(const char *filename, QEMUOptionParameter *options,
return ret;
}
- ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR,
+ &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
diff --git a/block/curl.c b/block/curl.c
index 5a46f9707c..a8075847b8 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -34,6 +34,11 @@
#define DPRINTF(fmt, ...) do { } while (0)
#endif
+#if LIBCURL_VERSION_NUM >= 0x071000
+/* The multi interface timer callback was introduced in 7.16.0 */
+#define NEED_CURL_TIMER_CALLBACK
+#endif
+
#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
CURLPROTO_FTP | CURLPROTO_FTPS | \
CURLPROTO_TFTP)
@@ -77,6 +82,7 @@ typedef struct CURLState
typedef struct BDRVCURLState {
CURLM *multi;
+ QEMUTimer timer;
size_t len;
CURLState states[CURL_NUM_STATES];
char *url;
@@ -87,6 +93,23 @@ typedef struct BDRVCURLState {
static void curl_clean_state(CURLState *s);
static void curl_multi_do(void *arg);
+#ifdef NEED_CURL_TIMER_CALLBACK
+static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
+{
+ BDRVCURLState *s = opaque;
+
+ DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
+ if (timeout_ms == -1) {
+ timer_del(&s->timer);
+ } else {
+ int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000;
+ timer_mod(&s->timer,
+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns);
+ }
+ return 0;
+}
+#endif
+
static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
void *s, void *sp)
{
@@ -209,20 +232,10 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
return FIND_RET_NONE;
}
-static void curl_multi_do(void *arg)
+static void curl_multi_read(BDRVCURLState *s)
{
- BDRVCURLState *s = (BDRVCURLState *)arg;
- int running;
- int r;
int msgs_in_queue;
- if (!s->multi)
- return;
-
- do {
- r = curl_multi_socket_all(s->multi, &running);
- } while(r == CURLM_CALL_MULTI_PERFORM);
-
/* Try to find done transfers, so we can free the easy
* handle again. */
do {
@@ -266,6 +279,41 @@ static void curl_multi_do(void *arg)
} while(msgs_in_queue);
}
+static void curl_multi_do(void *arg)
+{
+ BDRVCURLState *s = (BDRVCURLState *)arg;
+ int running;
+ int r;
+
+ if (!s->multi) {
+ return;
+ }
+
+ do {
+ r = curl_multi_socket_all(s->multi, &running);
+ } while(r == CURLM_CALL_MULTI_PERFORM);
+
+ curl_multi_read(s);
+}
+
+static void curl_multi_timeout_do(void *arg)
+{
+#ifdef NEED_CURL_TIMER_CALLBACK
+ BDRVCURLState *s = (BDRVCURLState *)arg;
+ int running;
+
+ if (!s->multi) {
+ return;
+ }
+
+ curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
+
+ curl_multi_read(s);
+#else
+ abort();
+#endif
+}
+
static CURLState *curl_init_state(BDRVCURLState *s)
{
CURLState *state = NULL;
@@ -413,7 +461,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
return -EROFS;
}
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -473,12 +521,20 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
curl_easy_cleanup(state->curl);
state->curl = NULL;
+ aio_timer_init(bdrv_get_aio_context(bs), &s->timer,
+ QEMU_CLOCK_REALTIME, SCALE_NS,
+ curl_multi_timeout_do, s);
+
// Now we know the file exists and its size, so let's
// initialize the multi interface!
s->multi = curl_multi_init();
curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s);
curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
+#ifdef NEED_CURL_TIMER_CALLBACK
+ curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
+ curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
+#endif
curl_multi_do(s);
qemu_opts_del(opts);
@@ -597,6 +653,9 @@ static void curl_close(BlockDriverState *bs)
}
if (s->multi)
curl_multi_cleanup(s->multi);
+
+ timer_del(&s->timer);
+
g_free(s->url);
}
diff --git a/block/gluster.c b/block/gluster.c
index 877686a7fe..a009b15ded 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -21,19 +21,15 @@
#include "qemu/uri.h"
typedef struct GlusterAIOCB {
- BlockDriverAIOCB common;
int64_t size;
int ret;
- bool *finished;
QEMUBH *bh;
+ Coroutine *coroutine;
} GlusterAIOCB;
typedef struct BDRVGlusterState {
struct glfs *glfs;
- int fds[2];
struct glfs_fd *fd;
- int event_reader_pos;
- GlusterAIOCB *event_acb;
} BDRVGlusterState;
#define GLUSTER_FD_READ 0
@@ -231,46 +227,32 @@ out:
return NULL;
}
-static void qemu_gluster_complete_aio(GlusterAIOCB *acb, BDRVGlusterState *s)
+static void qemu_gluster_complete_aio(void *opaque)
{
- int ret;
- bool *finished = acb->finished;
- BlockDriverCompletionFunc *cb = acb->common.cb;
- void *opaque = acb->common.opaque;
-
- if (!acb->ret || acb->ret == acb->size) {
- ret = 0; /* Success */
- } else if (acb->ret < 0) {
- ret = acb->ret; /* Read/Write failed */
- } else {
- ret = -EIO; /* Partial read/write - fail it */
- }
+ GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
- qemu_aio_release(acb);
- cb(opaque, ret);
- if (finished) {
- *finished = true;
- }
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+ qemu_coroutine_enter(acb->coroutine, NULL);
}
-static void qemu_gluster_aio_event_reader(void *opaque)
+/*
+ * AIO callback routine called from GlusterFS thread.
+ */
+static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
{
- BDRVGlusterState *s = opaque;
- ssize_t ret;
-
- do {
- char *p = (char *)&s->event_acb;
-
- ret = read(s->fds[GLUSTER_FD_READ], p + s->event_reader_pos,
- sizeof(s->event_acb) - s->event_reader_pos);
- if (ret > 0) {
- s->event_reader_pos += ret;
- if (s->event_reader_pos == sizeof(s->event_acb)) {
- s->event_reader_pos = 0;
- qemu_gluster_complete_aio(s->event_acb, s);
- }
- }
- } while (ret < 0 && errno == EINTR);
+ GlusterAIOCB *acb = (GlusterAIOCB *)arg;
+
+ if (!ret || ret == acb->size) {
+ acb->ret = 0; /* Success */
+ } else if (ret < 0) {
+ acb->ret = ret; /* Read/Write failed */
+ } else {
+ acb->ret = -EIO; /* Partial read/write - fail it */
+ }
+
+ acb->bh = qemu_bh_new(qemu_gluster_complete_aio, acb);
+ qemu_bh_schedule(acb->bh);
}
/* TODO Convert to fine grained options */
@@ -298,7 +280,7 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
Error *local_err = NULL;
const char *filename;
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -309,7 +291,6 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
filename = qemu_opt_get(opts, "filename");
-
s->glfs = qemu_gluster_init(gconf, filename);
if (!s->glfs) {
ret = -errno;
@@ -329,18 +310,8 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
s->fd = glfs_open(s->glfs, gconf->image, open_flags);
if (!s->fd) {
ret = -errno;
- goto out;
}
- ret = qemu_pipe(s->fds);
- if (ret < 0) {
- ret = -errno;
- goto out;
- }
- fcntl(s->fds[GLUSTER_FD_READ], F_SETFL, O_NONBLOCK);
- qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ],
- qemu_gluster_aio_event_reader, NULL, s);
-
out:
qemu_opts_del(opts);
qemu_gluster_gconf_free(gconf);
@@ -356,12 +327,65 @@ out:
return ret;
}
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+static coroutine_fn int qemu_gluster_co_write_zeroes(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
+{
+ int ret;
+ GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
+ BDRVGlusterState *s = bs->opaque;
+ off_t size = nb_sectors * BDRV_SECTOR_SIZE;
+ off_t offset = sector_num * BDRV_SECTOR_SIZE;
+
+ acb->size = size;
+ acb->ret = 0;
+ acb->coroutine = qemu_coroutine_self();
+
+ ret = glfs_zerofill_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
+ if (ret < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ qemu_coroutine_yield();
+ ret = acb->ret;
+
+out:
+ g_slice_free(GlusterAIOCB, acb);
+ return ret;
+}
+
+static inline bool gluster_supports_zerofill(void)
+{
+ return 1;
+}
+
+static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
+ int64_t size)
+{
+ return glfs_zerofill(fd, offset, size);
+}
+
+#else
+static inline bool gluster_supports_zerofill(void)
+{
+ return 0;
+}
+
+static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
+ int64_t size)
+{
+ return 0;
+}
+#endif
+
static int qemu_gluster_create(const char *filename,
QEMUOptionParameter *options, Error **errp)
{
struct glfs *glfs;
struct glfs_fd *fd;
int ret = 0;
+ int prealloc = 0;
int64_t total_size = 0;
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
@@ -374,6 +398,19 @@ static int qemu_gluster_create(const char *filename,
while (options && options->name) {
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
total_size = options->value.n / BDRV_SECTOR_SIZE;
+ } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
+ if (!options->value.s || !strcmp(options->value.s, "off")) {
+ prealloc = 0;
+ } else if (!strcmp(options->value.s, "full") &&
+ gluster_supports_zerofill()) {
+ prealloc = 1;
+ } else {
+ error_setg(errp, "Invalid preallocation mode: '%s'"
+ " or GlusterFS doesn't support zerofill API",
+ options->value.s);
+ ret = -EINVAL;
+ goto out;
+ }
}
options++;
}
@@ -383,9 +420,15 @@ static int qemu_gluster_create(const char *filename,
if (!fd) {
ret = -errno;
} else {
- if (glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
+ if (!glfs_ftruncate(fd, total_size * BDRV_SECTOR_SIZE)) {
+ if (prealloc && qemu_gluster_zerofill(fd, 0,
+ total_size * BDRV_SECTOR_SIZE)) {
+ ret = -errno;
+ }
+ } else {
ret = -errno;
}
+
if (glfs_close(fd) != 0) {
ret = -errno;
}
@@ -398,58 +441,18 @@ out:
return ret;
}
-static void qemu_gluster_aio_cancel(BlockDriverAIOCB *blockacb)
-{
- GlusterAIOCB *acb = (GlusterAIOCB *)blockacb;
- bool finished = false;
-
- acb->finished = &finished;
- while (!finished) {
- qemu_aio_wait();
- }
-}
-
-static const AIOCBInfo gluster_aiocb_info = {
- .aiocb_size = sizeof(GlusterAIOCB),
- .cancel = qemu_gluster_aio_cancel,
-};
-
-static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
-{
- GlusterAIOCB *acb = (GlusterAIOCB *)arg;
- BlockDriverState *bs = acb->common.bs;
- BDRVGlusterState *s = bs->opaque;
- int retval;
-
- acb->ret = ret;
- retval = qemu_write_full(s->fds[GLUSTER_FD_WRITE], &acb, sizeof(acb));
- if (retval != sizeof(acb)) {
- /*
- * Gluster AIO callback thread failed to notify the waiting
- * QEMU thread about IO completion.
- */
- error_report("Gluster AIO completion failed: %s", strerror(errno));
- abort();
- }
-}
-
-static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque, int write)
+static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int write)
{
int ret;
- GlusterAIOCB *acb;
+ GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
BDRVGlusterState *s = bs->opaque;
- size_t size;
- off_t offset;
-
- offset = sector_num * BDRV_SECTOR_SIZE;
- size = nb_sectors * BDRV_SECTOR_SIZE;
+ size_t size = nb_sectors * BDRV_SECTOR_SIZE;
+ off_t offset = sector_num * BDRV_SECTOR_SIZE;
- acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
acb->size = size;
acb->ret = 0;
- acb->finished = NULL;
+ acb->coroutine = qemu_coroutine_self();
if (write) {
ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
@@ -460,13 +463,16 @@ static BlockDriverAIOCB *qemu_gluster_aio_rw(BlockDriverState *bs,
}
if (ret < 0) {
+ ret = -errno;
goto out;
}
- return &acb->common;
+
+ qemu_coroutine_yield();
+ ret = acb->ret;
out:
- qemu_aio_release(acb);
- return NULL;
+ g_slice_free(GlusterAIOCB, acb);
+ return ret;
}
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
@@ -482,71 +488,68 @@ static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset)
return 0;
}
-static BlockDriverAIOCB *qemu_gluster_aio_readv(BlockDriverState *bs,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque)
+static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
{
- return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+ return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 0);
}
-static BlockDriverAIOCB *qemu_gluster_aio_writev(BlockDriverState *bs,
- int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb, void *opaque)
+static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
{
- return qemu_gluster_aio_rw(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+ return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
}
-static BlockDriverAIOCB *qemu_gluster_aio_flush(BlockDriverState *bs,
- BlockDriverCompletionFunc *cb, void *opaque)
+static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
{
int ret;
- GlusterAIOCB *acb;
+ GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
BDRVGlusterState *s = bs->opaque;
- acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
acb->size = 0;
acb->ret = 0;
- acb->finished = NULL;
+ acb->coroutine = qemu_coroutine_self();
ret = glfs_fsync_async(s->fd, &gluster_finish_aiocb, acb);
if (ret < 0) {
+ ret = -errno;
goto out;
}
- return &acb->common;
+
+ qemu_coroutine_yield();
+ ret = acb->ret;
out:
- qemu_aio_release(acb);
- return NULL;
+ g_slice_free(GlusterAIOCB, acb);
+ return ret;
}
#ifdef CONFIG_GLUSTERFS_DISCARD
-static BlockDriverAIOCB *qemu_gluster_aio_discard(BlockDriverState *bs,
- int64_t sector_num, int nb_sectors, BlockDriverCompletionFunc *cb,
- void *opaque)
+static coroutine_fn int qemu_gluster_co_discard(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors)
{
int ret;
- GlusterAIOCB *acb;
+ GlusterAIOCB *acb = g_slice_new(GlusterAIOCB);
BDRVGlusterState *s = bs->opaque;
- size_t size;
- off_t offset;
-
- offset = sector_num * BDRV_SECTOR_SIZE;
- size = nb_sectors * BDRV_SECTOR_SIZE;
+ size_t size = nb_sectors * BDRV_SECTOR_SIZE;
+ off_t offset = sector_num * BDRV_SECTOR_SIZE;
- acb = qemu_aio_get(&gluster_aiocb_info, bs, cb, opaque);
acb->size = 0;
acb->ret = 0;
- acb->finished = NULL;
+ acb->coroutine = qemu_coroutine_self();
ret = glfs_discard_async(s->fd, offset, size, &gluster_finish_aiocb, acb);
if (ret < 0) {
+ ret = -errno;
goto out;
}
- return &acb->common;
+
+ qemu_coroutine_yield();
+ ret = acb->ret;
out:
- qemu_aio_release(acb);
- return NULL;
+ g_slice_free(GlusterAIOCB, acb);
+ return ret;
}
#endif
@@ -581,10 +584,6 @@ static void qemu_gluster_close(BlockDriverState *bs)
{
BDRVGlusterState *s = bs->opaque;
- close(s->fds[GLUSTER_FD_READ]);
- close(s->fds[GLUSTER_FD_WRITE]);
- qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL);
-
if (s->fd) {
glfs_close(s->fd);
s->fd = NULL;
@@ -604,6 +603,11 @@ static QEMUOptionParameter qemu_gluster_create_options[] = {
.type = OPT_SIZE,
.help = "Virtual disk size"
},
+ {
+ .name = BLOCK_OPT_PREALLOC,
+ .type = OPT_STRING,
+ .help = "Preallocation mode (allowed values: off, full)"
+ },
{ NULL }
};
@@ -618,12 +622,15 @@ static BlockDriver bdrv_gluster = {
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate,
- .bdrv_aio_readv = qemu_gluster_aio_readv,
- .bdrv_aio_writev = qemu_gluster_aio_writev,
- .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .bdrv_co_readv = qemu_gluster_co_readv,
+ .bdrv_co_writev = qemu_gluster_co_writev,
+ .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_aio_discard = qemu_gluster_aio_discard,
+ .bdrv_co_discard = qemu_gluster_co_discard,
+#endif
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
#endif
.create_options = qemu_gluster_create_options,
};
@@ -639,12 +646,15 @@ static BlockDriver bdrv_gluster_tcp = {
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate,
- .bdrv_aio_readv = qemu_gluster_aio_readv,
- .bdrv_aio_writev = qemu_gluster_aio_writev,
- .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .bdrv_co_readv = qemu_gluster_co_readv,
+ .bdrv_co_writev = qemu_gluster_co_writev,
+ .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_aio_discard = qemu_gluster_aio_discard,
+ .bdrv_co_discard = qemu_gluster_co_discard,
+#endif
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
#endif
.create_options = qemu_gluster_create_options,
};
@@ -660,12 +670,15 @@ static BlockDriver bdrv_gluster_unix = {
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate,
- .bdrv_aio_readv = qemu_gluster_aio_readv,
- .bdrv_aio_writev = qemu_gluster_aio_writev,
- .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .bdrv_co_readv = qemu_gluster_co_readv,
+ .bdrv_co_writev = qemu_gluster_co_writev,
+ .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_aio_discard = qemu_gluster_aio_discard,
+ .bdrv_co_discard = qemu_gluster_co_discard,
+#endif
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
#endif
.create_options = qemu_gluster_create_options,
};
@@ -681,12 +694,15 @@ static BlockDriver bdrv_gluster_rdma = {
.bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate,
- .bdrv_aio_readv = qemu_gluster_aio_readv,
- .bdrv_aio_writev = qemu_gluster_aio_writev,
- .bdrv_aio_flush = qemu_gluster_aio_flush,
+ .bdrv_co_readv = qemu_gluster_co_readv,
+ .bdrv_co_writev = qemu_gluster_co_writev,
+ .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
#ifdef CONFIG_GLUSTERFS_DISCARD
- .bdrv_aio_discard = qemu_gluster_aio_discard,
+ .bdrv_co_discard = qemu_gluster_co_discard,
+#endif
+#ifdef CONFIG_GLUSTERFS_ZEROFILL
+ .bdrv_co_write_zeroes = qemu_gluster_co_write_zeroes,
#endif
.create_options = qemu_gluster_create_options,
};
diff --git a/block/iscsi.c b/block/iscsi.c
index 829d444733..6f4af72a75 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -68,6 +68,7 @@ typedef struct IscsiTask {
int do_retry;
struct scsi_task *task;
Coroutine *co;
+ QEMUBH *bh;
} IscsiTask;
typedef struct IscsiAIOCB {
@@ -123,6 +124,13 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
qemu_bh_schedule(acb->bh);
}
+static void iscsi_co_generic_bh_cb(void *opaque)
+{
+ struct IscsiTask *iTask = opaque;
+ qemu_bh_delete(iTask->bh);
+ qemu_coroutine_enter(iTask->co, NULL);
+}
+
static void
iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque)
@@ -147,7 +155,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
out:
if (iTask->co) {
- qemu_coroutine_enter(iTask->co, NULL);
+ iTask->bh = qemu_bh_new(iscsi_co_generic_bh_cb, iTask);
+ qemu_bh_schedule(iTask->bh);
}
}
@@ -239,44 +248,6 @@ iscsi_process_write(void *arg)
iscsi_set_events(iscsilun);
}
-static int
-iscsi_aio_writev_acb(IscsiAIOCB *acb);
-
-static void
-iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status,
- void *command_data, void *opaque)
-{
- IscsiAIOCB *acb = opaque;
-
- trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled);
-
- g_free(acb->buf);
- acb->buf = NULL;
-
- if (acb->canceled != 0) {
- return;
- }
-
- acb->status = 0;
- if (status != 0) {
- if (status == SCSI_STATUS_CHECK_CONDITION
- && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
- && acb->retries-- > 0) {
- scsi_free_scsi_task(acb->task);
- acb->task = NULL;
- if (iscsi_aio_writev_acb(acb) == 0) {
- iscsi_set_events(acb->iscsilun);
- return;
- }
- }
- error_report("Failed to write16 data to iSCSI lun. %s",
- iscsi_get_error(iscsi));
- acb->status = -EIO;
- }
-
- iscsi_schedule_bh(acb);
-}
-
static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
{
return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
@@ -301,324 +272,175 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
return 1;
}
-static int
-iscsi_aio_writev_acb(IscsiAIOCB *acb)
+static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
{
- struct iscsi_context *iscsi = acb->iscsilun->iscsi;
- size_t size;
- uint32_t num_sectors;
+ IscsiLun *iscsilun = bs->opaque;
+ struct IscsiTask iTask;
uint64_t lba;
-#if !defined(LIBISCSI_FEATURE_IOVECTOR)
- struct iscsi_data data;
-#endif
- int ret;
-
- acb->canceled = 0;
- acb->bh = NULL;
- acb->status = -EINPROGRESS;
- acb->buf = NULL;
+ uint32_t num_sectors;
+ uint8_t *data = NULL;
+ uint8_t *buf = NULL;
- /* this will allow us to get rid of 'buf' completely */
- size = acb->nb_sectors * BDRV_SECTOR_SIZE;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return -EINVAL;
+ }
+ lba = sector_qemu2lun(sector_num, iscsilun);
+ num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
- data.size = MIN(size, acb->qiov->size);
-
/* if the iovec only contains one buffer we can pass it directly */
- if (acb->qiov->niov == 1) {
- data.data = acb->qiov->iov[0].iov_base;
+ if (iov->niov == 1) {
+ data = iov->iov[0].iov_base;
} else {
- acb->buf = g_malloc(data.size);
- qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size);
- data.data = acb->buf;
+ size_t size = MIN(nb_sectors * BDRV_SECTOR_SIZE, iov->size);
+ buf = g_malloc(size);
+ qemu_iovec_to_buf(iov, 0, buf, size);
+ data = buf;
}
#endif
-
- acb->task = malloc(sizeof(struct scsi_task));
- if (acb->task == NULL) {
- error_report("iSCSI: Failed to allocate task for scsi WRITE16 "
- "command. %s", iscsi_get_error(iscsi));
- return -1;
+ iscsi_co_init_iscsitask(iscsilun, &iTask);
+retry:
+ iTask.task = iscsi_write16_task(iscsilun->iscsi, iscsilun->lun, lba,
+ data, num_sectors * iscsilun->block_size,
+ iscsilun->block_size, 0, 0, 0, 0, 0,
+ iscsi_co_generic_cb, &iTask);
+ if (iTask.task == NULL) {
+ g_free(buf);
+ return -ENOMEM;
}
- memset(acb->task, 0, sizeof(struct scsi_task));
-
- acb->task->xfer_dir = SCSI_XFER_WRITE;
- acb->task->cdb_size = 16;
- acb->task->cdb[0] = 0x8a;
- lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
- *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
- *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
- num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
- *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
- acb->task->expxferlen = size;
-
#if defined(LIBISCSI_FEATURE_IOVECTOR)
- ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
- iscsi_aio_write16_cb,
- NULL,
- acb);
-#else
- ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
- iscsi_aio_write16_cb,
- &data,
- acb);
+ scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
+ iov->niov);
#endif
- if (ret != 0) {
- scsi_free_scsi_task(acb->task);
- g_free(acb->buf);
- return -1;
+ while (!iTask.complete) {
+ iscsi_set_events(iscsilun);
+ qemu_coroutine_yield();
}
-#if defined(LIBISCSI_FEATURE_IOVECTOR)
- scsi_task_set_iov_out(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
-#endif
-
- return 0;
-}
-
-static BlockDriverAIOCB *
-iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
- QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb,
- void *opaque)
-{
- IscsiLun *iscsilun = bs->opaque;
- IscsiAIOCB *acb;
-
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
- return NULL;
+ if (iTask.task != NULL) {
+ scsi_free_scsi_task(iTask.task);
+ iTask.task = NULL;
}
- acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
- trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
-
- acb->iscsilun = iscsilun;
- acb->qiov = qiov;
- acb->nb_sectors = nb_sectors;
- acb->sector_num = sector_num;
- acb->retries = ISCSI_CMD_RETRIES;
-
- if (iscsi_aio_writev_acb(acb) != 0) {
- qemu_aio_release(acb);
- return NULL;
+ if (iTask.do_retry) {
+ goto retry;
}
- iscsi_set_events(iscsilun);
- return &acb->common;
-}
-
-static int
-iscsi_aio_readv_acb(IscsiAIOCB *acb);
-
-static void
-iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status,
- void *command_data, void *opaque)
-{
- IscsiAIOCB *acb = opaque;
-
- trace_iscsi_aio_read16_cb(iscsi, status, acb, acb->canceled);
-
- if (acb->canceled != 0) {
- return;
- }
+ g_free(buf);
- acb->status = 0;
- if (status != 0) {
- if (status == SCSI_STATUS_CHECK_CONDITION
- && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
- && acb->retries-- > 0) {
- scsi_free_scsi_task(acb->task);
- acb->task = NULL;
- if (iscsi_aio_readv_acb(acb) == 0) {
- iscsi_set_events(acb->iscsilun);
- return;
- }
- }
- error_report("Failed to read16 data from iSCSI lun. %s",
- iscsi_get_error(iscsi));
- acb->status = -EIO;
+ if (iTask.status != SCSI_STATUS_GOOD) {
+ return -EIO;
}
- iscsi_schedule_bh(acb);
+ return 0;
}
-static int
-iscsi_aio_readv_acb(IscsiAIOCB *acb)
+static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors,
+ QEMUIOVector *iov)
{
- struct iscsi_context *iscsi = acb->iscsilun->iscsi;
- size_t size;
+ IscsiLun *iscsilun = bs->opaque;
+ struct IscsiTask iTask;
uint64_t lba;
uint32_t num_sectors;
- int ret;
#if !defined(LIBISCSI_FEATURE_IOVECTOR)
int i;
#endif
- acb->canceled = 0;
- acb->bh = NULL;
- acb->status = -EINPROGRESS;
- acb->buf = NULL;
-
- size = acb->nb_sectors * BDRV_SECTOR_SIZE;
-
- acb->task = malloc(sizeof(struct scsi_task));
- if (acb->task == NULL) {
- error_report("iSCSI: Failed to allocate task for scsi READ16 "
- "command. %s", iscsi_get_error(iscsi));
- return -1;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return -EINVAL;
}
- memset(acb->task, 0, sizeof(struct scsi_task));
- acb->task->xfer_dir = SCSI_XFER_READ;
- acb->task->expxferlen = size;
- lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
- num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
+ lba = sector_qemu2lun(sector_num, iscsilun);
+ num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
- switch (acb->iscsilun->type) {
+ iscsi_co_init_iscsitask(iscsilun, &iTask);
+retry:
+ switch (iscsilun->type) {
case TYPE_DISK:
- acb->task->cdb_size = 16;
- acb->task->cdb[0] = 0x88;
- *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
- *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
- *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
+ iTask.task = iscsi_read16_task(iscsilun->iscsi, iscsilun->lun, lba,
+ num_sectors * iscsilun->block_size,
+ iscsilun->block_size, 0, 0, 0, 0, 0,
+ iscsi_co_generic_cb, &iTask);
break;
default:
- acb->task->cdb_size = 10;
- acb->task->cdb[0] = 0x28;
- *(uint32_t *)&acb->task->cdb[2] = htonl(lba);
- *(uint16_t *)&acb->task->cdb[7] = htons(num_sectors);
+ iTask.task = iscsi_read10_task(iscsilun->iscsi, iscsilun->lun, lba,
+ num_sectors * iscsilun->block_size,
+ iscsilun->block_size,
+#if !defined(CONFIG_LIBISCSI_1_4) /* API change from 1.4.0 to 1.5.0 */
+ 0, 0, 0, 0, 0,
+#endif
+ iscsi_co_generic_cb, &iTask);
break;
}
-
- ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task,
- iscsi_aio_read16_cb,
- NULL,
- acb);
- if (ret != 0) {
- scsi_free_scsi_task(acb->task);
- return -1;
+ if (iTask.task == NULL) {
+ return -ENOMEM;
}
-
#if defined(LIBISCSI_FEATURE_IOVECTOR)
- scsi_task_set_iov_in(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov);
+ scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
#else
- for (i = 0; i < acb->qiov->niov; i++) {
- scsi_task_add_data_in_buffer(acb->task,
- acb->qiov->iov[i].iov_len,
- acb->qiov->iov[i].iov_base);
+ for (i = 0; i < iov->niov; i++) {
+ scsi_task_add_data_in_buffer(iTask.task,
+ iov->iov[i].iov_len,
+ iov->iov[i].iov_base);
}
#endif
- return 0;
-}
-static BlockDriverAIOCB *
-iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
- QEMUIOVector *qiov, int nb_sectors,
- BlockDriverCompletionFunc *cb,
- void *opaque)
-{
- IscsiLun *iscsilun = bs->opaque;
- IscsiAIOCB *acb;
-
- if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
- return NULL;
- }
-
- acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
- trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
-
- acb->nb_sectors = nb_sectors;
- acb->sector_num = sector_num;
- acb->iscsilun = iscsilun;
- acb->qiov = qiov;
- acb->retries = ISCSI_CMD_RETRIES;
-
- if (iscsi_aio_readv_acb(acb) != 0) {
- qemu_aio_release(acb);
- return NULL;
+ while (!iTask.complete) {
+ iscsi_set_events(iscsilun);
+ qemu_coroutine_yield();
}
- iscsi_set_events(iscsilun);
- return &acb->common;
-}
-
-static int
-iscsi_aio_flush_acb(IscsiAIOCB *acb);
-
-static void
-iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
- void *command_data, void *opaque)
-{
- IscsiAIOCB *acb = opaque;
-
- if (acb->canceled != 0) {
- return;
+ if (iTask.task != NULL) {
+ scsi_free_scsi_task(iTask.task);
+ iTask.task = NULL;
}
- acb->status = 0;
- if (status != 0) {
- if (status == SCSI_STATUS_CHECK_CONDITION
- && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
- && acb->retries-- > 0) {
- scsi_free_scsi_task(acb->task);
- acb->task = NULL;
- if (iscsi_aio_flush_acb(acb) == 0) {
- iscsi_set_events(acb->iscsilun);
- return;
- }
- }
- error_report("Failed to sync10 data on iSCSI lun. %s",
- iscsi_get_error(iscsi));
- acb->status = -EIO;
+ if (iTask.do_retry) {
+ goto retry;
}
- iscsi_schedule_bh(acb);
-}
-
-static int
-iscsi_aio_flush_acb(IscsiAIOCB *acb)
-{
- struct iscsi_context *iscsi = acb->iscsilun->iscsi;
-
- acb->canceled = 0;
- acb->bh = NULL;
- acb->status = -EINPROGRESS;
- acb->buf = NULL;
-
- acb->task = iscsi_synchronizecache10_task(iscsi, acb->iscsilun->lun,
- 0, 0, 0, 0,
- iscsi_synccache10_cb,
- acb);
- if (acb->task == NULL) {
- error_report("iSCSI: Failed to send synchronizecache10 command. %s",
- iscsi_get_error(iscsi));
- return -1;
+ if (iTask.status != SCSI_STATUS_GOOD) {
+ return -EIO;
}
return 0;
}
-static BlockDriverAIOCB *
-iscsi_aio_flush(BlockDriverState *bs,
- BlockDriverCompletionFunc *cb, void *opaque)
+static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
{
IscsiLun *iscsilun = bs->opaque;
+ struct IscsiTask iTask;
- IscsiAIOCB *acb;
+ iscsi_co_init_iscsitask(iscsilun, &iTask);
- acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
+retry:
+ if (iscsi_synchronizecache10_task(iscsilun->iscsi, iscsilun->lun, 0, 0, 0,
+ 0, iscsi_co_generic_cb, &iTask) == NULL) {
+ return -ENOMEM;
+ }
- acb->iscsilun = iscsilun;
- acb->retries = ISCSI_CMD_RETRIES;
+ while (!iTask.complete) {
+ iscsi_set_events(iscsilun);
+ qemu_coroutine_yield();
+ }
- if (iscsi_aio_flush_acb(acb) != 0) {
- qemu_aio_release(acb);
- return NULL;
+ if (iTask.task != NULL) {
+ scsi_free_scsi_task(iTask.task);
+ iTask.task = NULL;
}
- iscsi_set_events(iscsilun);
+ if (iTask.do_retry) {
+ goto retry;
+ }
- return &acb->common;
+ if (iTask.status != SCSI_STATUS_GOOD) {
+ return -EIO;
+ }
+
+ return 0;
}
#ifdef __linux__
@@ -847,7 +669,7 @@ retry:
sector_qemu2lun(sector_num, iscsilun),
8 + 16, iscsi_co_generic_cb,
&iTask) == NULL) {
- ret = -EIO;
+ ret = -ENOMEM;
goto out;
}
@@ -931,7 +753,7 @@ coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
retry:
if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
iscsi_co_generic_cb, &iTask) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
while (!iTask.complete) {
@@ -1000,7 +822,7 @@ retry:
iscsilun->zeroblock, iscsilun->block_size,
nb_blocks, 0, !!(flags & BDRV_REQ_MAY_UNMAP),
0, 0, iscsi_co_generic_cb, &iTask) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
while (!iTask.complete) {
@@ -1299,7 +1121,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
return -EINVAL;
}
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -1395,6 +1217,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
+ bs->request_alignment = iscsilun->block_size;
/* Medium changer or tape. We dont have any emulation for this so this must
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
@@ -1443,23 +1266,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
sizeof(struct scsi_inquiry_block_limits));
scsi_free_scsi_task(task);
task = NULL;
-
- if (iscsilun->bl.max_unmap < 0xffffffff) {
- bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap,
- iscsilun);
- }
- bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
- iscsilun);
-
- if (iscsilun->bl.max_ws_len < 0xffffffff) {
- bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len,
- iscsilun);
- }
- bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
- iscsilun);
-
- bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
- iscsilun);
}
#if defined(LIBISCSI_FEATURE_NOP_COUNTER)
@@ -1504,6 +1310,41 @@ static void iscsi_close(BlockDriverState *bs)
memset(iscsilun, 0, sizeof(IscsiLun));
}
+static int iscsi_refresh_limits(BlockDriverState *bs)
+{
+ IscsiLun *iscsilun = bs->opaque;
+
+ /* We don't actually refresh here, but just return data queried in
+ * iscsi_open(): iscsi targets don't change their limits. */
+ if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
+ if (iscsilun->bl.max_unmap < 0xffffffff) {
+ bs->bl.max_discard = sector_lun2qemu(iscsilun->bl.max_unmap,
+ iscsilun);
+ }
+ bs->bl.discard_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
+ iscsilun);
+
+ if (iscsilun->bl.max_ws_len < 0xffffffff) {
+ bs->bl.max_write_zeroes = sector_lun2qemu(iscsilun->bl.max_ws_len,
+ iscsilun);
+ }
+ bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran,
+ iscsilun);
+
+ bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len,
+ iscsilun);
+ }
+ return 0;
+}
+
+/* We have nothing to do for iSCSI reopen, stub just returns
+ * success */
+static int iscsi_reopen_prepare(BDRVReopenState *state,
+ BlockReopenQueue *queue, Error **errp)
+{
+ return 0;
+}
+
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
{
IscsiLun *iscsilun = bs->opaque;
@@ -1612,10 +1453,12 @@ static BlockDriver bdrv_iscsi = {
.bdrv_close = iscsi_close,
.bdrv_create = iscsi_create,
.create_options = iscsi_create_options,
+ .bdrv_reopen_prepare = iscsi_reopen_prepare,
.bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info,
.bdrv_truncate = iscsi_truncate,
+ .bdrv_refresh_limits = iscsi_refresh_limits,
#if defined(LIBISCSI_FEATURE_IOVECTOR)
.bdrv_co_get_block_status = iscsi_co_get_block_status,
@@ -1624,10 +1467,9 @@ static BlockDriver bdrv_iscsi = {
#if defined(SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED)
.bdrv_co_write_zeroes = iscsi_co_write_zeroes,
#endif
-
- .bdrv_aio_readv = iscsi_aio_readv,
- .bdrv_aio_writev = iscsi_aio_writev,
- .bdrv_aio_flush = iscsi_aio_flush,
+ .bdrv_co_readv = iscsi_co_readv,
+ .bdrv_co_writev = iscsi_co_writev,
+ .bdrv_co_flush_to_disk = iscsi_co_flush,
#ifdef __linux__
.bdrv_ioctl = iscsi_ioctl,
diff --git a/block/mirror.c b/block/mirror.c
index 6dc27ad35d..2a4333474e 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -31,7 +31,8 @@ typedef struct MirrorBlockJob {
BlockJob common;
RateLimit limit;
BlockDriverState *target;
- MirrorSyncMode mode;
+ BlockDriverState *base;
+ bool is_none_mode;
BlockdevOnError on_source_error, on_target_error;
bool synced;
bool should_complete;
@@ -95,6 +96,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
}
+ qemu_iovec_destroy(&op->qiov);
g_slice_free(MirrorOp, op);
qemu_coroutine_enter(s->common.co, NULL);
}
@@ -335,10 +337,9 @@ static void coroutine_fn mirror_run(void *opaque)
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
mirror_free_init(s);
- if (s->mode != MIRROR_SYNC_MODE_NONE) {
+ if (!s->is_none_mode) {
/* First part, loop on the sectors and initialize the dirty bitmap. */
- BlockDriverState *base;
- base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
+ BlockDriverState *base = s->base;
for (sector_num = 0; sector_num < end; ) {
int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1;
ret = bdrv_is_allocated_above(bs, base,
@@ -481,8 +482,14 @@ immediate_exit:
bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL);
}
bdrv_swap(s->target, s->common.bs);
+ if (s->common.driver->job_type == BLOCK_JOB_TYPE_COMMIT) {
+ /* drop the bs loop chain formed by the swap: break the loop then
+ * trigger the unref from the top one */
+ BlockDriverState *p = s->base->backing_hd;
+ s->base->backing_hd = NULL;
+ bdrv_unref(p);
+ }
}
- bdrv_close(s->target);
bdrv_unref(s->target);
block_job_completed(&s->common, ret);
}
@@ -536,12 +543,24 @@ static const BlockJobDriver mirror_job_driver = {
.complete = mirror_complete,
};
-void mirror_start(BlockDriverState *bs, BlockDriverState *target,
- int64_t speed, int64_t granularity, int64_t buf_size,
- MirrorSyncMode mode, BlockdevOnError on_source_error,
- BlockdevOnError on_target_error,
- BlockDriverCompletionFunc *cb,
- void *opaque, Error **errp)
+static const BlockJobDriver commit_active_job_driver = {
+ .instance_size = sizeof(MirrorBlockJob),
+ .job_type = BLOCK_JOB_TYPE_COMMIT,
+ .set_speed = mirror_set_speed,
+ .iostatus_reset
+ = mirror_iostatus_reset,
+ .complete = mirror_complete,
+};
+
+static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
+ int64_t speed, int64_t granularity,
+ int64_t buf_size,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp,
+ const BlockJobDriver *driver,
+ bool is_none_mode, BlockDriverState *base)
{
MirrorBlockJob *s;
@@ -566,7 +585,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
return;
}
- s = block_job_create(&mirror_job_driver, bs, speed, cb, opaque, errp);
+
+ s = block_job_create(driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
@@ -574,7 +594,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
s->on_source_error = on_source_error;
s->on_target_error = on_target_error;
s->target = target;
- s->mode = mode;
+ s->is_none_mode = is_none_mode;
+ s->base = base;
s->granularity = granularity;
s->buf_size = MAX(buf_size, granularity);
@@ -586,3 +607,73 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
trace_mirror_start(bs, s, s->common.co, opaque);
qemu_coroutine_enter(s->common.co, s);
}
+
+void mirror_start(BlockDriverState *bs, BlockDriverState *target,
+ int64_t speed, int64_t granularity, int64_t buf_size,
+ MirrorSyncMode mode, BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp)
+{
+ bool is_none_mode;
+ BlockDriverState *base;
+
+ is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
+ base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
+ mirror_start_job(bs, target, speed, granularity, buf_size,
+ on_source_error, on_target_error, cb, opaque, errp,
+ &mirror_job_driver, is_none_mode, base);
+}
+
+void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
+ int64_t speed,
+ BlockdevOnError on_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp)
+{
+ int64_t length, base_length;
+ int orig_base_flags;
+
+ orig_base_flags = bdrv_get_flags(base);
+
+ if (bdrv_reopen(base, bs->open_flags, errp)) {
+ return;
+ }
+
+ length = bdrv_getlength(bs);
+ if (length < 0) {
+ error_setg(errp, "Unable to determine length of %s", bs->filename);
+ goto error_restore_flags;
+ }
+
+ base_length = bdrv_getlength(base);
+ if (base_length < 0) {
+ error_setg(errp, "Unable to determine length of %s", base->filename);
+ goto error_restore_flags;
+ }
+
+ if (length > base_length) {
+ if (bdrv_truncate(base, length) < 0) {
+ error_setg(errp, "Top image %s is larger than base image %s, and "
+ "resize of base image failed",
+ bs->filename, base->filename);
+ goto error_restore_flags;
+ }
+ }
+
+ bdrv_ref(base);
+ mirror_start_job(bs, base, speed, 0, 0,
+ on_error, on_error, cb, opaque, errp,
+ &commit_active_job_driver, false, base);
+ if (error_is_set(errp)) {
+ goto error_restore_flags;
+ }
+
+ return;
+
+error_restore_flags:
+ /* ignore error and errp for bdrv_reopen, because we want to propagate
+ * the original error */
+ bdrv_reopen(base, orig_base_flags, NULL);
+ return;
+}
diff --git a/block/nbd-client.c b/block/nbd-client.c
new file mode 100644
index 0000000000..0922b78292
--- /dev/null
+++ b/block/nbd-client.c
@@ -0,0 +1,385 @@
+/*
+ * QEMU Block driver for NBD
+ *
+ * Copyright (C) 2008 Bull S.A.S.
+ * Author: Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ * Some parts:
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * 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 "nbd-client.h"
+#include "qemu/sockets.h"
+
+#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
+#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
+
+static void nbd_recv_coroutines_enter_all(NbdClientSession *s)
+{
+ int i;
+
+ for (i = 0; i < MAX_NBD_REQUESTS; i++) {
+ if (s->recv_coroutine[i]) {
+ qemu_coroutine_enter(s->recv_coroutine[i], NULL);
+ }
+ }
+}
+
+static void nbd_reply_ready(void *opaque)
+{
+ NbdClientSession *s = opaque;
+ uint64_t i;
+ int ret;
+
+ if (s->reply.handle == 0) {
+ /* No reply already in flight. Fetch a header. It is possible
+ * that another thread has done the same thing in parallel, so
+ * the socket is not readable anymore.
+ */
+ ret = nbd_receive_reply(s->sock, &s->reply);
+ if (ret == -EAGAIN) {
+ return;
+ }
+ if (ret < 0) {
+ s->reply.handle = 0;
+ goto fail;
+ }
+ }
+
+ /* There's no need for a mutex on the receive side, because the
+ * handler acts as a synchronization point and ensures that only
+ * one coroutine is called until the reply finishes. */
+ i = HANDLE_TO_INDEX(s, s->reply.handle);
+ if (i >= MAX_NBD_REQUESTS) {
+ goto fail;
+ }
+
+ if (s->recv_coroutine[i]) {
+ qemu_coroutine_enter(s->recv_coroutine[i], NULL);
+ return;
+ }
+
+fail:
+ nbd_recv_coroutines_enter_all(s);
+}
+
+static void nbd_restart_write(void *opaque)
+{
+ NbdClientSession *s = opaque;
+
+ qemu_coroutine_enter(s->send_coroutine, NULL);
+}
+
+static int nbd_co_send_request(NbdClientSession *s,
+ struct nbd_request *request,
+ QEMUIOVector *qiov, int offset)
+{
+ int rc, ret;
+
+ qemu_co_mutex_lock(&s->send_mutex);
+ s->send_coroutine = qemu_coroutine_self();
+ qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, s);
+ if (qiov) {
+ if (!s->is_unix) {
+ socket_set_cork(s->sock, 1);
+ }
+ rc = nbd_send_request(s->sock, request);
+ if (rc >= 0) {
+ ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
+ offset, request->len);
+ if (ret != request->len) {
+ rc = -EIO;
+ }
+ }
+ if (!s->is_unix) {
+ socket_set_cork(s->sock, 0);
+ }
+ } else {
+ rc = nbd_send_request(s->sock, request);
+ }
+ qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, s);
+ s->send_coroutine = NULL;
+ qemu_co_mutex_unlock(&s->send_mutex);
+ return rc;
+}
+
+static void nbd_co_receive_reply(NbdClientSession *s,
+ struct nbd_request *request, struct nbd_reply *reply,
+ QEMUIOVector *qiov, int offset)
+{
+ int ret;
+
+ /* Wait until we're woken up by the read handler. TODO: perhaps
+ * peek at the next reply and avoid yielding if it's ours? */
+ qemu_coroutine_yield();
+ *reply = s->reply;
+ if (reply->handle != request->handle) {
+ reply->error = EIO;
+ } else {
+ if (qiov && reply->error == 0) {
+ ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
+ offset, request->len);
+ if (ret != request->len) {
+ reply->error = EIO;
+ }
+ }
+
+ /* Tell the read handler to read another header. */
+ s->reply.handle = 0;
+ }
+}
+
+static void nbd_coroutine_start(NbdClientSession *s,
+ struct nbd_request *request)
+{
+ int i;
+
+ /* Poor man semaphore. The free_sema is locked when no other request
+ * can be accepted, and unlocked after receiving one reply. */
+ if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
+ qemu_co_mutex_lock(&s->free_sema);
+ assert(s->in_flight < MAX_NBD_REQUESTS);
+ }
+ s->in_flight++;
+
+ for (i = 0; i < MAX_NBD_REQUESTS; i++) {
+ if (s->recv_coroutine[i] == NULL) {
+ s->recv_coroutine[i] = qemu_coroutine_self();
+ break;
+ }
+ }
+
+ assert(i < MAX_NBD_REQUESTS);
+ request->handle = INDEX_TO_HANDLE(s, i);
+}
+
+static void nbd_coroutine_end(NbdClientSession *s,
+ struct nbd_request *request)
+{
+ int i = HANDLE_TO_INDEX(s, request->handle);
+ s->recv_coroutine[i] = NULL;
+ if (s->in_flight-- == MAX_NBD_REQUESTS) {
+ qemu_co_mutex_unlock(&s->free_sema);
+ }
+}
+
+static int nbd_co_readv_1(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov,
+ int offset)
+{
+ struct nbd_request request = { .type = NBD_CMD_READ };
+ struct nbd_reply reply;
+ ssize_t ret;
+
+ request.from = sector_num * 512;
+ request.len = nb_sectors * 512;
+
+ nbd_coroutine_start(client, &request);
+ ret = nbd_co_send_request(client, &request, NULL, 0);
+ if (ret < 0) {
+ reply.error = -ret;
+ } else {
+ nbd_co_receive_reply(client, &request, &reply, qiov, offset);
+ }
+ nbd_coroutine_end(client, &request);
+ return -reply.error;
+
+}
+
+static int nbd_co_writev_1(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov,
+ int offset)
+{
+ struct nbd_request request = { .type = NBD_CMD_WRITE };
+ struct nbd_reply reply;
+ ssize_t ret;
+
+ if (!bdrv_enable_write_cache(client->bs) &&
+ (client->nbdflags & NBD_FLAG_SEND_FUA)) {
+ request.type |= NBD_CMD_FLAG_FUA;
+ }
+
+ request.from = sector_num * 512;
+ request.len = nb_sectors * 512;
+
+ nbd_coroutine_start(client, &request);
+ ret = nbd_co_send_request(client, &request, qiov, offset);
+ if (ret < 0) {
+ reply.error = -ret;
+ } else {
+ nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ }
+ nbd_coroutine_end(client, &request);
+ return -reply.error;
+}
+
+/* qemu-nbd has a limit of slightly less than 1M per request. Try to
+ * remain aligned to 4K. */
+#define NBD_MAX_SECTORS 2040
+
+int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ int offset = 0;
+ int ret;
+ while (nb_sectors > NBD_MAX_SECTORS) {
+ ret = nbd_co_readv_1(client, sector_num,
+ NBD_MAX_SECTORS, qiov, offset);
+ if (ret < 0) {
+ return ret;
+ }
+ offset += NBD_MAX_SECTORS * 512;
+ sector_num += NBD_MAX_SECTORS;
+ nb_sectors -= NBD_MAX_SECTORS;
+ }
+ return nbd_co_readv_1(client, sector_num, nb_sectors, qiov, offset);
+}
+
+int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov)
+{
+ int offset = 0;
+ int ret;
+ while (nb_sectors > NBD_MAX_SECTORS) {
+ ret = nbd_co_writev_1(client, sector_num,
+ NBD_MAX_SECTORS, qiov, offset);
+ if (ret < 0) {
+ return ret;
+ }
+ offset += NBD_MAX_SECTORS * 512;
+ sector_num += NBD_MAX_SECTORS;
+ nb_sectors -= NBD_MAX_SECTORS;
+ }
+ return nbd_co_writev_1(client, sector_num, nb_sectors, qiov, offset);
+}
+
+int nbd_client_session_co_flush(NbdClientSession *client)
+{
+ struct nbd_request request = { .type = NBD_CMD_FLUSH };
+ struct nbd_reply reply;
+ ssize_t ret;
+
+ if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) {
+ return 0;
+ }
+
+ if (client->nbdflags & NBD_FLAG_SEND_FUA) {
+ request.type |= NBD_CMD_FLAG_FUA;
+ }
+
+ request.from = 0;
+ request.len = 0;
+
+ nbd_coroutine_start(client, &request);
+ ret = nbd_co_send_request(client, &request, NULL, 0);
+ if (ret < 0) {
+ reply.error = -ret;
+ } else {
+ nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ }
+ nbd_coroutine_end(client, &request);
+ return -reply.error;
+}
+
+int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors)
+{
+ struct nbd_request request = { .type = NBD_CMD_TRIM };
+ struct nbd_reply reply;
+ ssize_t ret;
+
+ if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) {
+ return 0;
+ }
+ request.from = sector_num * 512;
+ request.len = nb_sectors * 512;
+
+ nbd_coroutine_start(client, &request);
+ ret = nbd_co_send_request(client, &request, NULL, 0);
+ if (ret < 0) {
+ reply.error = -ret;
+ } else {
+ nbd_co_receive_reply(client, &request, &reply, NULL, 0);
+ }
+ nbd_coroutine_end(client, &request);
+ return -reply.error;
+
+}
+
+static void nbd_teardown_connection(NbdClientSession *client)
+{
+ struct nbd_request request = {
+ .type = NBD_CMD_DISC,
+ .from = 0,
+ .len = 0
+ };
+
+ nbd_send_request(client->sock, &request);
+
+ /* finish any pending coroutines */
+ shutdown(client->sock, 2);
+ nbd_recv_coroutines_enter_all(client);
+
+ qemu_aio_set_fd_handler(client->sock, NULL, NULL, NULL);
+ closesocket(client->sock);
+ client->sock = -1;
+}
+
+void nbd_client_session_close(NbdClientSession *client)
+{
+ if (!client->bs) {
+ return;
+ }
+
+ nbd_teardown_connection(client);
+ client->bs = NULL;
+}
+
+int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
+ int sock, const char *export)
+{
+ int ret;
+
+ /* NBD handshake */
+ logout("session init %s\n", export);
+ qemu_set_block(sock);
+ ret = nbd_receive_negotiate(sock, export,
+ &client->nbdflags, &client->size,
+ &client->blocksize);
+ if (ret < 0) {
+ logout("Failed to negotiate with the NBD server\n");
+ closesocket(sock);
+ return ret;
+ }
+
+ qemu_co_mutex_init(&client->send_mutex);
+ qemu_co_mutex_init(&client->free_sema);
+ client->bs = bs;
+ client->sock = sock;
+
+ /* Now that we're connected, set the socket to be non-blocking and
+ * kick the reply mechanism. */
+ qemu_set_nonblock(sock);
+ qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, client);
+
+ logout("Established connection with NBD server\n");
+ return 0;
+}
diff --git a/block/nbd-client.h b/block/nbd-client.h
new file mode 100644
index 0000000000..f2a63378bb
--- /dev/null
+++ b/block/nbd-client.h
@@ -0,0 +1,50 @@
+#ifndef NBD_CLIENT_H
+#define NBD_CLIENT_H
+
+#include "qemu-common.h"
+#include "block/nbd.h"
+#include "block/block_int.h"
+
+/* #define DEBUG_NBD */
+
+#if defined(DEBUG_NBD)
+#define logout(fmt, ...) \
+ fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+#define MAX_NBD_REQUESTS 16
+
+typedef struct NbdClientSession {
+ int sock;
+ uint32_t nbdflags;
+ off_t size;
+ size_t blocksize;
+
+ CoMutex send_mutex;
+ CoMutex free_sema;
+ Coroutine *send_coroutine;
+ int in_flight;
+
+ Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
+ struct nbd_reply reply;
+
+ bool is_unix;
+
+ BlockDriverState *bs;
+} NbdClientSession;
+
+int nbd_client_session_init(NbdClientSession *client, BlockDriverState *bs,
+ int sock, const char *export_name);
+void nbd_client_session_close(NbdClientSession *client);
+
+int nbd_client_session_co_discard(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors);
+int nbd_client_session_co_flush(NbdClientSession *client);
+int nbd_client_session_co_writev(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov);
+int nbd_client_session_co_readv(NbdClientSession *client, int64_t sector_num,
+ int nb_sectors, QEMUIOVector *qiov);
+
+#endif /* NBD_CLIENT_H */
diff --git a/block/nbd.c b/block/nbd.c
index c8deeee67f..327e913002 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -26,8 +26,7 @@
* THE SOFTWARE.
*/
-#include "qemu-common.h"
-#include "block/nbd.h"
+#include "block/nbd-client.h"
#include "qemu/uri.h"
#include "block/block_int.h"
#include "qemu/module.h"
@@ -40,37 +39,9 @@
#define EN_OPTSTR ":exportname="
-/* #define DEBUG_NBD */
-
-#if defined(DEBUG_NBD)
-#define logout(fmt, ...) \
- fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
-#else
-#define logout(fmt, ...) ((void)0)
-#endif
-
-#define MAX_NBD_REQUESTS 16
-#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
-#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
-
typedef struct BDRVNBDState {
- int sock;
- uint32_t nbdflags;
- off_t size;
- size_t blocksize;
-
- CoMutex send_mutex;
- CoMutex free_sema;
- Coroutine *send_coroutine;
- int in_flight;
-
- Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
- struct nbd_reply reply;
-
- bool is_unix;
+ NbdClientSession client;
QemuOpts *socket_opts;
-
- char *export_name; /* An NBD server may export several devices */
} BDRVNBDState;
static int nbd_parse_uri(const char *filename, QDict *options)
@@ -217,7 +188,7 @@ out:
g_free(file);
}
-static int nbd_config(BDRVNBDState *s, QDict *options)
+static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
{
Error *local_err = NULL;
@@ -227,14 +198,15 @@ static int nbd_config(BDRVNBDState *s, QDict *options)
"be used at the same time.");
return -EINVAL;
}
- s->is_unix = true;
+ s->client.is_unix = true;
} else if (qdict_haskey(options, "host")) {
- s->is_unix = false;
+ s->client.is_unix = false;
} else {
return -EINVAL;
}
- s->socket_opts = qemu_opts_create_nofail(&socket_optslist);
+ s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
+ &error_abort);
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
if (error_is_set(&local_err)) {
@@ -247,162 +219,20 @@ static int nbd_config(BDRVNBDState *s, QDict *options)
qemu_opt_set_number(s->socket_opts, "port", NBD_DEFAULT_PORT);
}
- s->export_name = g_strdup(qdict_get_try_str(options, "export"));
- if (s->export_name) {
+ *export = g_strdup(qdict_get_try_str(options, "export"));
+ if (*export) {
qdict_del(options, "export");
}
return 0;
}
-
-static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
-{
- int i;
-
- /* Poor man semaphore. The free_sema is locked when no other request
- * can be accepted, and unlocked after receiving one reply. */
- if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
- qemu_co_mutex_lock(&s->free_sema);
- assert(s->in_flight < MAX_NBD_REQUESTS);
- }
- s->in_flight++;
-
- for (i = 0; i < MAX_NBD_REQUESTS; i++) {
- if (s->recv_coroutine[i] == NULL) {
- s->recv_coroutine[i] = qemu_coroutine_self();
- break;
- }
- }
-
- assert(i < MAX_NBD_REQUESTS);
- request->handle = INDEX_TO_HANDLE(s, i);
-}
-
-static void nbd_reply_ready(void *opaque)
-{
- BDRVNBDState *s = opaque;
- uint64_t i;
- int ret;
-
- if (s->reply.handle == 0) {
- /* No reply already in flight. Fetch a header. It is possible
- * that another thread has done the same thing in parallel, so
- * the socket is not readable anymore.
- */
- ret = nbd_receive_reply(s->sock, &s->reply);
- if (ret == -EAGAIN) {
- return;
- }
- if (ret < 0) {
- s->reply.handle = 0;
- goto fail;
- }
- }
-
- /* There's no need for a mutex on the receive side, because the
- * handler acts as a synchronization point and ensures that only
- * one coroutine is called until the reply finishes. */
- i = HANDLE_TO_INDEX(s, s->reply.handle);
- if (i >= MAX_NBD_REQUESTS) {
- goto fail;
- }
-
- if (s->recv_coroutine[i]) {
- qemu_coroutine_enter(s->recv_coroutine[i], NULL);
- return;
- }
-
-fail:
- for (i = 0; i < MAX_NBD_REQUESTS; i++) {
- if (s->recv_coroutine[i]) {
- qemu_coroutine_enter(s->recv_coroutine[i], NULL);
- }
- }
-}
-
-static void nbd_restart_write(void *opaque)
-{
- BDRVNBDState *s = opaque;
- qemu_coroutine_enter(s->send_coroutine, NULL);
-}
-
-static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
- QEMUIOVector *qiov, int offset)
-{
- int rc, ret;
-
- qemu_co_mutex_lock(&s->send_mutex);
- s->send_coroutine = qemu_coroutine_self();
- qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, s);
- if (qiov) {
- if (!s->is_unix) {
- socket_set_cork(s->sock, 1);
- }
- rc = nbd_send_request(s->sock, request);
- if (rc >= 0) {
- ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
- if (ret != request->len) {
- rc = -EIO;
- }
- }
- if (!s->is_unix) {
- socket_set_cork(s->sock, 0);
- }
- } else {
- rc = nbd_send_request(s->sock, request);
- }
- qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, s);
- s->send_coroutine = NULL;
- qemu_co_mutex_unlock(&s->send_mutex);
- return rc;
-}
-
-static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
- struct nbd_reply *reply,
- QEMUIOVector *qiov, int offset)
-{
- int ret;
-
- /* Wait until we're woken up by the read handler. TODO: perhaps
- * peek at the next reply and avoid yielding if it's ours? */
- qemu_coroutine_yield();
- *reply = s->reply;
- if (reply->handle != request->handle) {
- reply->error = EIO;
- } else {
- if (qiov && reply->error == 0) {
- ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov,
- offset, request->len);
- if (ret != request->len) {
- reply->error = EIO;
- }
- }
-
- /* Tell the read handler to read another header. */
- s->reply.handle = 0;
- }
-}
-
-static void nbd_coroutine_end(BDRVNBDState *s, struct nbd_request *request)
-{
- int i = HANDLE_TO_INDEX(s, request->handle);
- s->recv_coroutine[i] = NULL;
- if (s->in_flight-- == MAX_NBD_REQUESTS) {
- qemu_co_mutex_unlock(&s->free_sema);
- }
-}
-
static int nbd_establish_connection(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
int sock;
- int ret;
- off_t size;
- size_t blocksize;
- if (s->is_unix) {
+ if (s->client.is_unix) {
sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
} else {
sock = tcp_socket_outgoing_opts(s->socket_opts);
@@ -417,53 +247,18 @@ static int nbd_establish_connection(BlockDriverState *bs)
return -errno;
}
- /* NBD handshake */
- ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
- &blocksize);
- if (ret < 0) {
- logout("Failed to negotiate with the NBD server\n");
- closesocket(sock);
- return ret;
- }
-
- /* Now that we're connected, set the socket to be non-blocking and
- * kick the reply mechanism. */
- qemu_set_nonblock(sock);
- qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL, s);
-
- s->sock = sock;
- s->size = size;
- s->blocksize = blocksize;
-
- logout("Established connection with NBD server\n");
- return 0;
-}
-
-static void nbd_teardown_connection(BlockDriverState *bs)
-{
- BDRVNBDState *s = bs->opaque;
- struct nbd_request request;
-
- request.type = NBD_CMD_DISC;
- request.from = 0;
- request.len = 0;
- nbd_send_request(s->sock, &request);
-
- qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL);
- closesocket(s->sock);
+ return sock;
}
static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVNBDState *s = bs->opaque;
- int result;
-
- qemu_co_mutex_init(&s->send_mutex);
- qemu_co_mutex_init(&s->free_sema);
+ char *export = NULL;
+ int result, sock;
/* Pop the config into our state object. Exit if invalid. */
- result = nbd_config(s, options);
+ result = nbd_config(s, options, &export);
if (result != 0) {
return result;
}
@@ -471,172 +266,64 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
/* establish TCP connection, return error if it fails
* TODO: Configurable retry-until-timeout behaviour.
*/
- result = nbd_establish_connection(bs);
-
- return result;
-}
-
-static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov,
- int offset)
-{
- BDRVNBDState *s = bs->opaque;
- struct nbd_request request;
- struct nbd_reply reply;
- ssize_t ret;
-
- request.type = NBD_CMD_READ;
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
-
- nbd_coroutine_start(s, &request);
- ret = nbd_co_send_request(s, &request, NULL, 0);
- if (ret < 0) {
- reply.error = -ret;
- } else {
- nbd_co_receive_reply(s, &request, &reply, qiov, offset);
- }
- nbd_coroutine_end(s, &request);
- return -reply.error;
-
-}
-
-static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
- int nb_sectors, QEMUIOVector *qiov,
- int offset)
-{
- BDRVNBDState *s = bs->opaque;
- struct nbd_request request;
- struct nbd_reply reply;
- ssize_t ret;
-
- request.type = NBD_CMD_WRITE;
- if (!bdrv_enable_write_cache(bs) && (s->nbdflags & NBD_FLAG_SEND_FUA)) {
- request.type |= NBD_CMD_FLAG_FUA;
+ sock = nbd_establish_connection(bs);
+ if (sock < 0) {
+ return sock;
}
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
-
- nbd_coroutine_start(s, &request);
- ret = nbd_co_send_request(s, &request, qiov, offset);
- if (ret < 0) {
- reply.error = -ret;
- } else {
- nbd_co_receive_reply(s, &request, &reply, NULL, 0);
- }
- nbd_coroutine_end(s, &request);
- return -reply.error;
+ /* NBD handshake */
+ result = nbd_client_session_init(&s->client, bs, sock, export);
+ g_free(export);
+ return result;
}
-/* qemu-nbd has a limit of slightly less than 1M per request. Try to
- * remain aligned to 4K. */
-#define NBD_MAX_SECTORS 2040
-
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
- int offset = 0;
- int ret;
- while (nb_sectors > NBD_MAX_SECTORS) {
- ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
- if (ret < 0) {
- return ret;
- }
- offset += NBD_MAX_SECTORS * 512;
- sector_num += NBD_MAX_SECTORS;
- nb_sectors -= NBD_MAX_SECTORS;
- }
- return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
+ BDRVNBDState *s = bs->opaque;
+
+ return nbd_client_session_co_readv(&s->client, sector_num,
+ nb_sectors, qiov);
}
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
- int offset = 0;
- int ret;
- while (nb_sectors > NBD_MAX_SECTORS) {
- ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
- if (ret < 0) {
- return ret;
- }
- offset += NBD_MAX_SECTORS * 512;
- sector_num += NBD_MAX_SECTORS;
- nb_sectors -= NBD_MAX_SECTORS;
- }
- return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset);
+ BDRVNBDState *s = bs->opaque;
+
+ return nbd_client_session_co_writev(&s->client, sector_num,
+ nb_sectors, qiov);
}
static int nbd_co_flush(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
- struct nbd_request request;
- struct nbd_reply reply;
- ssize_t ret;
-
- if (!(s->nbdflags & NBD_FLAG_SEND_FLUSH)) {
- return 0;
- }
-
- request.type = NBD_CMD_FLUSH;
- if (s->nbdflags & NBD_FLAG_SEND_FUA) {
- request.type |= NBD_CMD_FLAG_FUA;
- }
-
- request.from = 0;
- request.len = 0;
- nbd_coroutine_start(s, &request);
- ret = nbd_co_send_request(s, &request, NULL, 0);
- if (ret < 0) {
- reply.error = -ret;
- } else {
- nbd_co_receive_reply(s, &request, &reply, NULL, 0);
- }
- nbd_coroutine_end(s, &request);
- return -reply.error;
+ return nbd_client_session_co_flush(&s->client);
}
static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
BDRVNBDState *s = bs->opaque;
- struct nbd_request request;
- struct nbd_reply reply;
- ssize_t ret;
- if (!(s->nbdflags & NBD_FLAG_SEND_TRIM)) {
- return 0;
- }
- request.type = NBD_CMD_TRIM;
- request.from = sector_num * 512;
- request.len = nb_sectors * 512;
-
- nbd_coroutine_start(s, &request);
- ret = nbd_co_send_request(s, &request, NULL, 0);
- if (ret < 0) {
- reply.error = -ret;
- } else {
- nbd_co_receive_reply(s, &request, &reply, NULL, 0);
- }
- nbd_coroutine_end(s, &request);
- return -reply.error;
+ return nbd_client_session_co_discard(&s->client, sector_num,
+ nb_sectors);
}
static void nbd_close(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
- g_free(s->export_name);
- qemu_opts_del(s->socket_opts);
- nbd_teardown_connection(bs);
+ qemu_opts_del(s->socket_opts);
+ nbd_client_session_close(&s->client);
}
static int64_t nbd_getlength(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
- return s->size;
+ return s->client.size;
}
static BlockDriver bdrv_nbd = {
diff --git a/block/qapi.c b/block/qapi.c
index a32cb79db8..8f4134b40a 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -29,6 +29,60 @@
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp/types.h"
+BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
+{
+ BlockDeviceInfo *info = g_malloc0(sizeof(*info));
+
+ info->file = g_strdup(bs->filename);
+ info->ro = bs->read_only;
+ info->drv = g_strdup(bs->drv->format_name);
+ info->encrypted = bs->encrypted;
+ info->encryption_key_missing = bdrv_key_required(bs);
+
+ if (bs->node_name[0]) {
+ info->has_node_name = true;
+ info->node_name = g_strdup(bs->node_name);
+ }
+
+ if (bs->backing_file[0]) {
+ info->has_backing_file = true;
+ info->backing_file = g_strdup(bs->backing_file);
+ }
+
+ info->backing_file_depth = bdrv_get_backing_file_depth(bs);
+
+ if (bs->io_limits_enabled) {
+ ThrottleConfig cfg;
+ throttle_get_config(&bs->throttle_state, &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;
+
+ info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
+ info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
+ info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
+
+ info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
+ info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max;
+ info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
+ info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max;
+ info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
+ info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max;
+
+ info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
+ info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max;
+ info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
+ info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max;
+ info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
+ info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
+
+ info->has_iops_size = cfg.op_size;
+ info->iops_size = cfg.op_size;
+ }
+
+ return info;
+}
+
/*
* Returns 0 on success, with *p_list either set to describe snapshot
* information, or NULL because there are no snapshots. Returns -errno on
@@ -211,60 +265,7 @@ void bdrv_query_info(BlockDriverState *bs,
if (bs->drv) {
info->has_inserted = true;
- info->inserted = g_malloc0(sizeof(*info->inserted));
- info->inserted->file = g_strdup(bs->filename);
- info->inserted->ro = bs->read_only;
- info->inserted->drv = g_strdup(bs->drv->format_name);
- info->inserted->encrypted = bs->encrypted;
- info->inserted->encryption_key_missing = bdrv_key_required(bs);
-
- if (bs->backing_file[0]) {
- info->inserted->has_backing_file = true;
- info->inserted->backing_file = g_strdup(bs->backing_file);
- }
-
- info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
-
- if (bs->io_limits_enabled) {
- ThrottleConfig cfg;
- throttle_get_config(&bs->throttle_state, &cfg);
- info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
- info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
- info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
-
- info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
- info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
- info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
-
- info->inserted->has_bps_max =
- cfg.buckets[THROTTLE_BPS_TOTAL].max;
- info->inserted->bps_max =
- cfg.buckets[THROTTLE_BPS_TOTAL].max;
- info->inserted->has_bps_rd_max =
- cfg.buckets[THROTTLE_BPS_READ].max;
- info->inserted->bps_rd_max =
- cfg.buckets[THROTTLE_BPS_READ].max;
- info->inserted->has_bps_wr_max =
- cfg.buckets[THROTTLE_BPS_WRITE].max;
- info->inserted->bps_wr_max =
- cfg.buckets[THROTTLE_BPS_WRITE].max;
-
- info->inserted->has_iops_max =
- cfg.buckets[THROTTLE_OPS_TOTAL].max;
- info->inserted->iops_max =
- cfg.buckets[THROTTLE_OPS_TOTAL].max;
- info->inserted->has_iops_rd_max =
- cfg.buckets[THROTTLE_OPS_READ].max;
- info->inserted->iops_rd_max =
- cfg.buckets[THROTTLE_OPS_READ].max;
- info->inserted->has_iops_wr_max =
- cfg.buckets[THROTTLE_OPS_WRITE].max;
- info->inserted->iops_wr_max =
- cfg.buckets[THROTTLE_OPS_WRITE].max;
-
- info->inserted->has_iops_size = cfg.op_size;
- info->inserted->iops_size = cfg.op_size;
- }
+ info->inserted = bdrv_block_device_info(bs);
bs0 = bs;
p_image_info = &info->inserted->image;
@@ -318,6 +319,11 @@ BlockStats *bdrv_query_stats(const BlockDriverState *bs)
s->parent = bdrv_query_stats(bs->file);
}
+ if (bs->backing_hd) {
+ s->has_backing = true;
+ s->backing = bdrv_query_stats(bs->backing_hd);
+ }
+
return s;
}
diff --git a/block/qcow.c b/block/qcow.c
index c470e05f60..948b0c5601 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -691,7 +691,8 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options,
return ret;
}
- ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR,
+ &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
diff --git a/block/qcow2.c b/block/qcow2.c
index f29aa88671..2da62b8a90 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -669,7 +669,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Enable lazy_refcounts according to image and command line options */
- opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
+ opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -718,7 +718,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
}
qemu_opts_del(opts);
- bs->bl.write_zeroes_alignment = s->cluster_sectors;
if (s->use_lazy_refcounts && s->qcow_version < 3) {
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
@@ -751,6 +750,15 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
+static int qcow2_refresh_limits(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ bs->bl.write_zeroes_alignment = s->cluster_sectors;
+
+ return 0;
+}
+
static int qcow2_set_key(BlockDriverState *bs, const char *key)
{
BDRVQcowState *s = bs->opaque;
@@ -1483,7 +1491,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
return ret;
}
- ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return ret;
@@ -2268,6 +2276,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_change_backing_file = qcow2_change_backing_file,
+ .bdrv_refresh_limits = qcow2_refresh_limits,
.bdrv_invalidate_cache = qcow2_invalidate_cache,
.create_options = qcow2_create_options,
diff --git a/block/qcow2.h b/block/qcow2.h
index 303eb26629..b5b7d13630 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -340,11 +340,11 @@ typedef enum QCow2MetadataOverlap {
#define QCOW2_OL_ALL \
(QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2)
-#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
-#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
+#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL
+#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
-#define REFT_OFFSET_MASK 0xffffffffffffff00ULL
+#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
{
diff --git a/block/qed.c b/block/qed.c
index 450a1fa2e9..694e6e2ee0 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -495,7 +495,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
}
}
- bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
s->need_check_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
qed_need_check_timer_cb, s);
@@ -507,6 +506,15 @@ out:
return ret;
}
+static int bdrv_qed_refresh_limits(BlockDriverState *bs)
+{
+ BDRVQEDState *s = bs->opaque;
+
+ bs->bl.write_zeroes_alignment = s->header.cluster_size >> BDRV_SECTOR_BITS;
+
+ return 0;
+}
+
/* We have nothing to do for QED reopen, stubs just return
* success */
static int bdrv_qed_reopen_prepare(BDRVReopenState *state,
@@ -563,8 +571,8 @@ static int qed_create(const char *filename, uint32_t cluster_size,
return ret;
}
- ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB,
- &local_err);
+ ret = bdrv_file_open(&bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
@@ -1616,6 +1624,7 @@ static BlockDriver bdrv_qed = {
.bdrv_truncate = bdrv_qed_truncate,
.bdrv_getlength = bdrv_qed_getlength,
.bdrv_get_info = bdrv_qed_get_info,
+ .bdrv_refresh_limits = bdrv_qed_refresh_limits,
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
.bdrv_check = bdrv_qed_check,
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 10c6b34ba9..126a634e45 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -127,6 +127,8 @@ typedef struct BDRVRawState {
int fd;
int type;
int open_flags;
+ size_t buf_align;
+
#if defined(__linux__)
/* linux floppy specific */
int64_t fd_open_time;
@@ -213,6 +215,76 @@ static int raw_normalize_devicepath(const char **filename)
}
#endif
+static void raw_probe_alignment(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ char *buf;
+ unsigned int sector_size;
+
+ /* For /dev/sg devices the alignment is not really used.
+ With buffered I/O, we don't have any restrictions. */
+ if (bs->sg || !(s->open_flags & O_DIRECT)) {
+ bs->request_alignment = 1;
+ s->buf_align = 1;
+ return;
+ }
+
+ /* Try a few ioctls to get the right size */
+ bs->request_alignment = 0;
+ s->buf_align = 0;
+
+#ifdef BLKSSZGET
+ if (ioctl(s->fd, BLKSSZGET, &sector_size) >= 0) {
+ bs->request_alignment = sector_size;
+ }
+#endif
+#ifdef DKIOCGETBLOCKSIZE
+ if (ioctl(s->fd, DKIOCGETBLOCKSIZE, &sector_size) >= 0) {
+ bs->request_alignment = sector_size;
+ }
+#endif
+#ifdef DIOCGSECTORSIZE
+ if (ioctl(s->fd, DIOCGSECTORSIZE, &sector_size) >= 0) {
+ bs->request_alignment = sector_size;
+ }
+#endif
+#ifdef CONFIG_XFS
+ if (s->is_xfs) {
+ struct dioattr da;
+ if (xfsctl(NULL, s->fd, XFS_IOC_DIOINFO, &da) >= 0) {
+ bs->request_alignment = da.d_miniosz;
+ /* The kernel returns wrong information for d_mem */
+ /* s->buf_align = da.d_mem; */
+ }
+ }
+#endif
+
+ /* If we could not get the sizes so far, we can only guess them */
+ if (!s->buf_align) {
+ size_t align;
+ buf = qemu_memalign(MAX_BLOCKSIZE, 2 * MAX_BLOCKSIZE);
+ for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
+ if (pread(s->fd, buf + align, MAX_BLOCKSIZE, 0) >= 0) {
+ s->buf_align = align;
+ break;
+ }
+ }
+ qemu_vfree(buf);
+ }
+
+ if (!bs->request_alignment) {
+ size_t align;
+ buf = qemu_memalign(s->buf_align, MAX_BLOCKSIZE);
+ for (align = 512; align <= MAX_BLOCKSIZE; align <<= 1) {
+ if (pread(s->fd, buf, align, 0) >= 0) {
+ bs->request_alignment = align;
+ break;
+ }
+ }
+ qemu_vfree(buf);
+ }
+}
+
static void raw_parse_flags(int bdrv_flags, int *open_flags)
{
assert(open_flags != NULL);
@@ -287,7 +359,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
int fd, ret;
struct stat st;
- opts = qemu_opts_create_nofail(&raw_runtime_opts);
+ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -463,7 +535,6 @@ static int raw_reopen_prepare(BDRVReopenState *state,
return ret;
}
-
static void raw_reopen_commit(BDRVReopenState *state)
{
BDRVRawReopenState *raw_s = state->opaque;
@@ -499,23 +570,15 @@ static void raw_reopen_abort(BDRVReopenState *state)
state->opaque = NULL;
}
+static int raw_refresh_limits(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
-/* XXX: use host sector size if necessary with:
-#ifdef DIOCGSECTORSIZE
- {
- unsigned int sectorsize = 512;
- if (!ioctl(fd, DIOCGSECTORSIZE, &sectorsize) &&
- sectorsize > bufsize)
- bufsize = sectorsize;
- }
-#endif
-#ifdef CONFIG_COCOA
- uint32_t blockSize = 512;
- if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
- bufsize = blockSize;
- }
-#endif
-*/
+ raw_probe_alignment(bs);
+ bs->bl.opt_mem_alignment = s->buf_align;
+
+ return 0;
+}
static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb)
{
@@ -1363,6 +1426,7 @@ static BlockDriver bdrv_file = {
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = raw_aio_discard,
+ .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
@@ -1740,6 +1804,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = hdev_aio_discard,
+ .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
@@ -1871,6 +1936,7 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
+ .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
@@ -1981,6 +2047,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
+ .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
@@ -2110,6 +2177,7 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
+ .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
diff --git a/block/raw-win32.c b/block/raw-win32.c
index 2bad5a39b4..beb7f2395e 100644
--- a/block/raw-win32.c
+++ b/block/raw-win32.c
@@ -202,6 +202,35 @@ static int set_sparse(int fd)
NULL, 0, NULL, 0, &returned, NULL);
}
+static void raw_probe_alignment(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ DWORD sectorsPerCluster, freeClusters, totalClusters, count;
+ DISK_GEOMETRY_EX dg;
+ BOOL status;
+
+ if (s->type == FTYPE_CD) {
+ bs->request_alignment = 2048;
+ return;
+ }
+ if (s->type == FTYPE_HARDDISK) {
+ status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
+ NULL, 0, &dg, sizeof(dg), &count, NULL);
+ if (status != 0) {
+ bs->request_alignment = dg.Geometry.BytesPerSector;
+ return;
+ }
+ /* try GetDiskFreeSpace too */
+ }
+
+ if (s->drive_path[0]) {
+ GetDiskFreeSpace(s->drive_path, &sectorsPerCluster,
+ &dg.Geometry.BytesPerSector,
+ &freeClusters, &totalClusters);
+ bs->request_alignment = dg.Geometry.BytesPerSector;
+ }
+}
+
static void raw_parse_flags(int flags, int *access_flags, DWORD *overlapped)
{
assert(access_flags != NULL);
@@ -248,7 +277,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
s->type = FTYPE_FILE;
- opts = qemu_opts_create_nofail(&raw_runtime_opts);
+ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -269,6 +298,17 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
}
}
+ if (filename[0] && filename[1] == ':') {
+ snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", filename[0]);
+ } else if (filename[0] == '\\' && filename[1] == '\\') {
+ s->drive_path[0] = 0;
+ } else {
+ /* Relative path. */
+ char buf[MAX_PATH];
+ GetCurrentDirectory(MAX_PATH, buf);
+ snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", buf[0]);
+ }
+
s->hfile = CreateFile(filename, access_flags,
FILE_SHARE_READ, NULL,
OPEN_EXISTING, overlapped, NULL);
@@ -293,6 +333,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
s->aio = aio;
}
+ raw_probe_alignment(bs);
ret = 0;
fail:
qemu_opts_del(opts);
@@ -550,7 +591,8 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
const char *filename;
- QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
+ QemuOpts *opts = qemu_opts_create(&raw_runtime_opts, NULL, 0,
+ &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
diff --git a/block/rbd.c b/block/rbd.c
index 4a1ea5b5ce..121fae221e 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -95,18 +95,13 @@ typedef struct RADOSCB {
#define RBD_FD_WRITE 1
typedef struct BDRVRBDState {
- int fds[2];
rados_t cluster;
rados_ioctx_t io_ctx;
rbd_image_t image;
char name[RBD_MAX_IMAGE_NAME_SIZE];
char *snap;
- int event_reader_pos;
- RADOSCB *event_rcb;
} BDRVRBDState;
-static void rbd_aio_bh_cb(void *opaque);
-
static int qemu_rbd_next_tok(char *dst, int dst_len,
char *src, char delim,
const char *name,
@@ -369,9 +364,8 @@ static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
}
/*
- * This aio completion is being called from qemu_rbd_aio_event_reader()
- * and runs in qemu context. It schedules a bh, but just in case the aio
- * was not cancelled before.
+ * This aio completion is being called from rbd_finish_bh() and runs in qemu
+ * BH context.
*/
static void qemu_rbd_complete_aio(RADOSCB *rcb)
{
@@ -401,36 +395,19 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
acb->ret = r;
}
}
- /* Note that acb->bh can be NULL in case where the aio was cancelled */
- acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
- qemu_bh_schedule(acb->bh);
- g_free(rcb);
-}
-/*
- * aio fd read handler. It runs in the qemu context and calls the
- * completion handling of completed rados aio operations.
- */
-static void qemu_rbd_aio_event_reader(void *opaque)
-{
- BDRVRBDState *s = opaque;
+ g_free(rcb);
- ssize_t ret;
+ if (acb->cmd == RBD_AIO_READ) {
+ qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
+ }
+ qemu_vfree(acb->bounce);
+ acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
+ acb->status = 0;
- do {
- char *p = (char *)&s->event_rcb;
-
- /* now read the rcb pointer that was sent from a non qemu thread */
- ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos,
- sizeof(s->event_rcb) - s->event_reader_pos);
- if (ret > 0) {
- s->event_reader_pos += ret;
- if (s->event_reader_pos == sizeof(s->event_rcb)) {
- s->event_reader_pos = 0;
- qemu_rbd_complete_aio(s->event_rcb);
- }
- }
- } while (ret < 0 && errno == EINTR);
+ if (!acb->cancelled) {
+ qemu_aio_release(acb);
+ }
}
/* TODO Convert to fine grained options */
@@ -461,7 +438,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
const char *filename;
int r;
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -538,23 +515,9 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
bs->read_only = (s->snap != NULL);
- s->event_reader_pos = 0;
- r = qemu_pipe(s->fds);
- if (r < 0) {
- error_report("error opening eventfd");
- goto failed;
- }
- fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
- fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
- qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
- NULL, s);
-
-
qemu_opts_del(opts);
return 0;
-failed:
- rbd_close(s->image);
failed_open:
rados_ioctx_destroy(s->io_ctx);
failed_shutdown:
@@ -569,10 +532,6 @@ static void qemu_rbd_close(BlockDriverState *bs)
{
BDRVRBDState *s = bs->opaque;
- close(s->fds[0]);
- close(s->fds[1]);
- qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL);
-
rbd_close(s->image);
rados_ioctx_destroy(s->io_ctx);
g_free(s->snap);
@@ -600,34 +559,11 @@ static const AIOCBInfo rbd_aiocb_info = {
.cancel = qemu_rbd_aio_cancel,
};
-static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
+static void rbd_finish_bh(void *opaque)
{
- int ret = 0;
- while (1) {
- fd_set wfd;
- int fd = s->fds[RBD_FD_WRITE];
-
- /* send the op pointer to the qemu thread that is responsible
- for the aio/op completion. Must do it in a qemu thread context */
- ret = write(fd, (void *)&rcb, sizeof(rcb));
- if (ret >= 0) {
- break;
- }
- if (errno == EINTR) {
- continue;
- }
- if (errno != EAGAIN) {
- break;
- }
-
- FD_ZERO(&wfd);
- FD_SET(fd, &wfd);
- do {
- ret = select(fd + 1, NULL, &wfd, NULL, NULL);
- } while (ret < 0 && errno == EINTR);
- }
-
- return ret;
+ RADOSCB *rcb = opaque;
+ qemu_bh_delete(rcb->acb->bh);
+ qemu_rbd_complete_aio(rcb);
}
/*
@@ -635,40 +571,18 @@ static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
*
* Note: this function is being called from a non qemu thread so
* we need to be careful about what we do here. Generally we only
- * write to the block notification pipe, and do the rest of the
- * io completion handling from qemu_rbd_aio_event_reader() which
- * runs in a qemu context.
+ * schedule a BH, and do the rest of the io completion handling
+ * from rbd_finish_bh() which runs in a qemu context.
*/
static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
{
- int ret;
+ RBDAIOCB *acb = rcb->acb;
+
rcb->ret = rbd_aio_get_return_value(c);
rbd_aio_release(c);
- ret = qemu_rbd_send_pipe(rcb->s, rcb);
- if (ret < 0) {
- error_report("failed writing to acb->s->fds");
- g_free(rcb);
- }
-}
-
-/* Callback when all queued rbd_aio requests are complete */
-static void rbd_aio_bh_cb(void *opaque)
-{
- RBDAIOCB *acb = opaque;
-
- if (acb->cmd == RBD_AIO_READ) {
- qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
- }
- qemu_vfree(acb->bounce);
- acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
- qemu_bh_delete(acb->bh);
- acb->bh = NULL;
- acb->status = 0;
-
- if (!acb->cancelled) {
- qemu_aio_release(acb);
- }
+ acb->bh = qemu_bh_new(rbd_finish_bh, rcb);
+ qemu_bh_schedule(acb->bh);
}
static int rbd_aio_discard_wrapper(rbd_image_t image,
diff --git a/block/sheepdog.c b/block/sheepdog.c
index b4ae50f44d..672b9c97a2 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -161,7 +161,7 @@ typedef struct SheepdogVdiReq {
uint32_t id;
uint32_t data_length;
uint64_t vdi_size;
- uint32_t vdi_id;
+ uint32_t base_vdi_id;
uint8_t copies;
uint8_t copy_policy;
uint8_t reserved[2];
@@ -1383,7 +1383,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
s->bs = bs;
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -1493,7 +1493,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot)
memset(&hdr, 0, sizeof(hdr));
hdr.opcode = SD_OP_NEW_VDI;
- hdr.vdi_id = s->inode.vdi_id;
+ hdr.base_vdi_id = s->inode.vdi_id;
wlen = SD_MAX_VDI_LEN;
@@ -1534,7 +1534,7 @@ static int sd_prealloc(const char *filename)
Error *local_err = NULL;
int ret;
- ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
@@ -1666,9 +1666,11 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
goto out;
}
} else if (!strcmp(options->name, BLOCK_OPT_REDUNDANCY)) {
- ret = parse_redundancy(s, options->value.s);
- if (ret < 0) {
- goto out;
+ if (options->value.s) {
+ ret = parse_redundancy(s, options->value.s);
+ if (ret < 0) {
+ goto out;
+ }
}
}
options++;
@@ -1682,7 +1684,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
if (backing_file) {
BlockDriverState *bs;
- BDRVSheepdogState *s;
+ BDRVSheepdogState *base;
BlockDriver *drv;
/* Currently, only Sheepdog backing image is supported. */
@@ -1693,22 +1695,22 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
goto out;
}
- ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err);
+ ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto out;
}
- s = bs->opaque;
+ base = bs->opaque;
- if (!is_snapshot(&s->inode)) {
+ if (!is_snapshot(&base->inode)) {
error_report("cannot clone from a non snapshot vdi");
bdrv_unref(bs);
ret = -EINVAL;
goto out;
}
-
+ s->inode.vdi_id = base->inode.vdi_id;
bdrv_unref(bs);
}
@@ -1741,7 +1743,7 @@ static void sd_close(BlockDriverState *bs)
memset(&hdr, 0, sizeof(hdr));
hdr.opcode = SD_OP_RELEASE_VDI;
- hdr.vdi_id = s->inode.vdi_id;
+ hdr.base_vdi_id = s->inode.vdi_id;
wlen = strlen(s->name) + 1;
hdr.data_length = wlen;
hdr.flags = SD_FLAG_CMD_WRITE;
@@ -1844,7 +1846,7 @@ static bool sd_delete(BDRVSheepdogState *s)
unsigned int wlen = SD_MAX_VDI_LEN, rlen = 0;
SheepdogVdiReq hdr = {
.opcode = SD_OP_DEL_VDI,
- .vdi_id = s->inode.vdi_id,
+ .base_vdi_id = s->inode.vdi_id,
.data_length = wlen,
.flags = SD_FLAG_CMD_WRITE,
};
@@ -2046,13 +2048,14 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
{
SheepdogAIOCB *acb;
int ret;
+ int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
+ BDRVSheepdogState *s = bs->opaque;
- if (bs->growable && sector_num + nb_sectors > bs->total_sectors) {
- ret = sd_truncate(bs, (sector_num + nb_sectors) * BDRV_SECTOR_SIZE);
+ if (bs->growable && offset > s->inode.vdi_size) {
+ ret = sd_truncate(bs, offset);
if (ret < 0) {
return ret;
}
- bs->total_sectors = sector_num + nb_sectors;
}
acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors);
@@ -2439,11 +2442,12 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
{
BDRVSheepdogState *s = bs->opaque;
SheepdogInode *inode = &s->inode;
- unsigned long start = sector_num * BDRV_SECTOR_SIZE / SD_DATA_OBJ_SIZE,
+ uint64_t offset = sector_num * BDRV_SECTOR_SIZE;
+ unsigned long start = offset / SD_DATA_OBJ_SIZE,
end = DIV_ROUND_UP((sector_num + nb_sectors) *
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
unsigned long idx;
- int64_t ret = BDRV_BLOCK_DATA;
+ int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
for (idx = start; idx < end; idx++) {
if (inode->data_vdi_id[idx] == 0) {
diff --git a/block/stream.c b/block/stream.c
index 46bec7d379..dd0b4ac3d2 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -75,6 +75,8 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
unused->backing_hd = NULL;
bdrv_unref(unused);
}
+
+ bdrv_refresh_limits(top);
}
static void coroutine_fn stream_run(void *opaque)
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index ee5583c309..8c9ae0d8e7 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -706,7 +706,8 @@ exit:
*
* If read-only, we must replay the log in RAM (or refuse to open
* a dirty VHDX file read-only) */
-int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed)
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
+ Error **errp)
{
int ret = 0;
VHDXHeader *hdr;
@@ -761,6 +762,16 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed)
}
if (logs.valid) {
+ if (bs->read_only) {
+ ret = -EPERM;
+ error_setg_errno(errp, EPERM,
+ "VHDX image file '%s' opened read-only, but "
+ "contains a log that needs to be replayed. To "
+ "replay the log, execute:\n qemu-img check -r "
+ "all '%s'",
+ bs->filename, bs->filename);
+ goto exit;
+ }
/* now flush the log */
ret = vhdx_log_flush(bs, s, &logs);
if (ret < 0) {
diff --git a/block/vhdx.c b/block/vhdx.c
index 67bbe103a1..9ee0a612ff 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -878,7 +878,6 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
int ret = 0;
uint32_t i;
uint64_t signature;
- bool log_flushed = false;
s->bat = NULL;
@@ -907,7 +906,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- ret = vhdx_parse_log(bs, s, &log_flushed);
+ ret = vhdx_parse_log(bs, s, &s->log_replayed_on_open, errp);
if (ret < 0) {
goto fail;
}
@@ -1798,7 +1797,7 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
goto exit;
}
- ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto exit;
@@ -1854,6 +1853,24 @@ exit:
return ret;
}
+/* If opened r/w, the VHDX driver will automatically replay the log,
+ * if one is present, inside the vhdx_open() call.
+ *
+ * If qemu-img check -r all is called, the image is automatically opened
+ * r/w and any log has already been replayed, so there is nothing (currently)
+ * for us to do here
+ */
+static int vhdx_check(BlockDriverState *bs, BdrvCheckResult *result,
+ BdrvCheckMode fix)
+{
+ BDRVVHDXState *s = bs->opaque;
+
+ if (s->log_replayed_on_open) {
+ result->corruptions_fixed++;
+ }
+ return 0;
+}
+
static QEMUOptionParameter vhdx_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -1898,6 +1915,7 @@ static BlockDriver bdrv_vhdx = {
.bdrv_co_writev = vhdx_co_writev,
.bdrv_create = vhdx_create,
.bdrv_get_info = vhdx_get_info,
+ .bdrv_check = vhdx_check,
.create_options = vhdx_create_options,
};
diff --git a/block/vhdx.h b/block/vhdx.h
index 51183b243c..2acd7c2d19 100644
--- a/block/vhdx.h
+++ b/block/vhdx.h
@@ -394,6 +394,8 @@ typedef struct BDRVVHDXState {
Error *migration_blocker;
+ bool log_replayed_on_open;
+
QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions;
} BDRVVHDXState;
@@ -408,7 +410,8 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset);
-int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed);
+int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
+ Error **errp);
int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
void *data, uint32_t length, uint64_t offset);
diff --git a/block/vmdk.c b/block/vmdk.c
index 0734bc200c..99ca60fdb9 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -428,10 +428,6 @@ static int vmdk_add_extent(BlockDriverState *bs,
extent->l2_size = l2_size;
extent->cluster_sectors = flat ? sectors : cluster_sectors;
- if (!flat) {
- bs->bl.write_zeroes_alignment =
- MAX(bs->bl.write_zeroes_alignment, cluster_sectors);
- }
if (s->num_extents > 1) {
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
} else {
@@ -640,6 +636,13 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
if (le32_to_cpu(header.flags) & VMDK4_FLAG_RGD) {
l1_backup_offset = le64_to_cpu(header.rgd_offset) << 9;
}
+ if (bdrv_getlength(file) <
+ le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
+ error_report("File truncated, expecting at least %lld bytes",
+ le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
+ return -EINVAL;
+ }
+
ret = vmdk_add_extent(bs, file, false,
le64_to_cpu(header.capacity),
le64_to_cpu(header.gd_offset) << 9,
@@ -654,6 +657,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
}
extent->compressed =
le16_to_cpu(header.compressAlgorithm) == VMDK4_COMPRESSION_DEFLATE;
+ if (extent->compressed) {
+ g_free(s->create_type);
+ s->create_type = g_strdup("streamOptimized");
+ }
extent->has_marker = le32_to_cpu(header.flags) & VMDK4_FLAG_MARKER;
extent->version = le32_to_cpu(header.version);
extent->has_zero_grain = le32_to_cpu(header.flags) & VMDK4_FLAG_ZERO_GRAIN;
@@ -749,9 +756,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
return -EINVAL;
}
} else if (!strcmp(type, "VMFS")) {
- flat_offset = 0;
+ if (ret == 4) {
+ flat_offset = 0;
+ } else {
+ error_setg(errp, "Invalid extent lines:\n%s", p);
+ return -EINVAL;
+ }
} else if (ret != 4) {
- error_setg(errp, "Invalid extent lines: \n%s", p);
+ error_setg(errp, "Invalid extent lines:\n%s", p);
return -EINVAL;
}
@@ -764,8 +776,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
path_combine(extent_path, sizeof(extent_path),
desc_file_path, fname);
- ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
- errp);
+ ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL,
+ bs->open_flags, errp);
if (ret) {
return ret;
}
@@ -886,6 +898,23 @@ fail:
return ret;
}
+
+static int vmdk_refresh_limits(BlockDriverState *bs)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int i;
+
+ for (i = 0; i < s->num_extents; i++) {
+ if (!s->extents[i].flat) {
+ bs->bl.write_zeroes_alignment =
+ MAX(bs->bl.write_zeroes_alignment,
+ s->extents[i].cluster_sectors);
+ }
+ }
+
+ return 0;
+}
+
static int get_whole_cluster(BlockDriverState *bs,
VmdkExtent *extent,
uint64_t cluster_offset,
@@ -1320,8 +1349,8 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
{
BDRVVmdkState *s = bs->opaque;
VmdkExtent *extent = NULL;
- int n, ret;
- int64_t index_in_cluster;
+ int ret;
+ int64_t index_in_cluster, n;
uint64_t extent_begin_sector, extent_relative_sector_num;
uint64_t cluster_offset;
VmdkMetaData m_data;
@@ -1447,23 +1476,33 @@ static int coroutine_fn vmdk_co_write_zeroes(BlockDriverState *bs,
}
static int vmdk_create_extent(const char *filename, int64_t filesize,
- bool flat, bool compress, bool zeroed_grain)
+ bool flat, bool compress, bool zeroed_grain,
+ Error **errp)
{
int ret, i;
- int fd = 0;
+ BlockDriverState *bs = NULL;
VMDK4Header header;
- uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
+ Error *local_err;
+ uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
+ uint32_t *gd_buf = NULL;
+ int gd_buf_size;
- fd = qemu_open(filename,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
- 0644);
- if (fd < 0) {
- return -errno;
+ ret = bdrv_create_file(filename, NULL, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto exit;
+ }
+
+ ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto exit;
}
+
if (flat) {
- ret = ftruncate(fd, filesize);
+ ret = bdrv_truncate(bs, filesize);
if (ret < 0) {
- ret = -errno;
+ error_setg(errp, "Could not truncate file");
}
goto exit;
}
@@ -1474,24 +1513,23 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
| (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0)
| (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0);
header.compressAlgorithm = compress ? VMDK4_COMPRESSION_DEFLATE : 0;
- header.capacity = filesize / 512;
+ header.capacity = filesize / BDRV_SECTOR_SIZE;
header.granularity = 128;
- header.num_gtes_per_gt = 512;
+ header.num_gtes_per_gt = BDRV_SECTOR_SIZE;
- grains = (filesize / 512 + header.granularity - 1) / header.granularity;
- gt_size = ((header.num_gtes_per_gt * sizeof(uint32_t)) + 511) >> 9;
- gt_count =
- (grains + header.num_gtes_per_gt - 1) / header.num_gtes_per_gt;
- gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
+ grains = DIV_ROUND_UP(filesize / BDRV_SECTOR_SIZE, header.granularity);
+ gt_size = DIV_ROUND_UP(header.num_gtes_per_gt * sizeof(uint32_t),
+ BDRV_SECTOR_SIZE);
+ gt_count = DIV_ROUND_UP(grains, header.num_gtes_per_gt);
+ gd_sectors = DIV_ROUND_UP(gt_count * sizeof(uint32_t), BDRV_SECTOR_SIZE);
header.desc_offset = 1;
header.desc_size = 20;
header.rgd_offset = header.desc_offset + header.desc_size;
- header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
+ header.gd_offset = header.rgd_offset + gd_sectors + (gt_size * gt_count);
header.grain_offset =
- ((header.gd_offset + gd_size + (gt_size * gt_count) +
- header.granularity - 1) / header.granularity) *
- header.granularity;
+ ROUND_UP(header.gd_offset + gd_sectors + (gt_size * gt_count),
+ header.granularity);
/* swap endianness for all header fields */
header.version = cpu_to_le32(header.version);
header.flags = cpu_to_le32(header.flags);
@@ -1511,48 +1549,55 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
header.check_bytes[3] = 0xa;
/* write all the data */
- ret = qemu_write_full(fd, &magic, sizeof(magic));
- if (ret != sizeof(magic)) {
- ret = -errno;
+ ret = bdrv_pwrite(bs, 0, &magic, sizeof(magic));
+ if (ret < 0) {
+ error_set(errp, QERR_IO_ERROR);
goto exit;
}
- ret = qemu_write_full(fd, &header, sizeof(header));
- if (ret != sizeof(header)) {
- ret = -errno;
+ ret = bdrv_pwrite(bs, sizeof(magic), &header, sizeof(header));
+ if (ret < 0) {
+ error_set(errp, QERR_IO_ERROR);
goto exit;
}
- ret = ftruncate(fd, le64_to_cpu(header.grain_offset) << 9);
+ ret = bdrv_truncate(bs, le64_to_cpu(header.grain_offset) << 9);
if (ret < 0) {
- ret = -errno;
+ error_setg(errp, "Could not truncate file");
goto exit;
}
/* write grain directory */
- lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
- for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_size;
+ gd_buf_size = gd_sectors * BDRV_SECTOR_SIZE;
+ gd_buf = g_malloc0(gd_buf_size);
+ for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_sectors;
i < gt_count; i++, tmp += gt_size) {
- ret = qemu_write_full(fd, &tmp, sizeof(tmp));
- if (ret != sizeof(tmp)) {
- ret = -errno;
- goto exit;
- }
+ gd_buf[i] = cpu_to_le32(tmp);
+ }
+ ret = bdrv_pwrite(bs, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
+ gd_buf, gd_buf_size);
+ if (ret < 0) {
+ error_set(errp, QERR_IO_ERROR);
+ goto exit;
}
/* write backup grain directory */
- lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
- for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_size;
+ for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_sectors;
i < gt_count; i++, tmp += gt_size) {
- ret = qemu_write_full(fd, &tmp, sizeof(tmp));
- if (ret != sizeof(tmp)) {
- ret = -errno;
- goto exit;
- }
+ gd_buf[i] = cpu_to_le32(tmp);
+ }
+ ret = bdrv_pwrite(bs, le64_to_cpu(header.gd_offset) * BDRV_SECTOR_SIZE,
+ gd_buf, gd_buf_size);
+ if (ret < 0) {
+ error_set(errp, QERR_IO_ERROR);
+ goto exit;
}
ret = 0;
- exit:
- qemu_close(fd);
+exit:
+ if (bs) {
+ bdrv_unref(bs);
+ }
+ g_free(gd_buf);
return ret;
}
@@ -1599,7 +1644,9 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
static int vmdk_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{
- int fd, idx = 0;
+ int idx = 0;
+ BlockDriverState *new_bs = NULL;
+ Error *local_err;
char *desc = NULL;
int64_t total_size = 0, filesize;
const char *adapter_type = NULL;
@@ -1616,6 +1663,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
uint32_t parent_cid = 0xffffffff;
uint32_t number_heads = 16;
bool zeroed_grain = false;
+ uint32_t desc_offset = 0, desc_len;
const char desc_template[] =
"# Disk DescriptorFile\n"
"version=1\n"
@@ -1749,7 +1797,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
path, desc_filename);
if (vmdk_create_extent(ext_filename, size,
- flat, compress, zeroed_grain)) {
+ flat, compress, zeroed_grain, errp)) {
ret = -EINVAL;
goto exit;
}
@@ -1757,7 +1805,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
/* Format description line */
snprintf(desc_line, sizeof(desc_line),
- desc_extent_line, size / 512, desc_filename);
+ desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename);
g_string_append(ext_desc_lines, desc_line);
}
/* generate descriptor file */
@@ -1768,36 +1816,43 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
parent_desc_line,
ext_desc_lines->str,
(flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
- total_size / (int64_t)(63 * number_heads * 512),
+ total_size /
+ (int64_t)(63 * number_heads * BDRV_SECTOR_SIZE),
number_heads,
adapter_type);
- if (split || flat) {
- fd = qemu_open(filename,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
- 0644);
+ desc_len = strlen(desc);
+ /* the descriptor offset = 0x200 */
+ if (!split && !flat) {
+ desc_offset = 0x200;
} else {
- fd = qemu_open(filename,
- O_WRONLY | O_BINARY | O_LARGEFILE,
- 0644);
+ ret = bdrv_create_file(filename, options, &local_err);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not create image file");
+ goto exit;
+ }
}
- if (fd < 0) {
- ret = -errno;
+ ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write description");
goto exit;
}
- /* the descriptor offset = 0x200 */
- if (!split && !flat && 0x200 != lseek(fd, 0x200, SEEK_SET)) {
- ret = -errno;
- goto close_exit;
+ ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not write description");
+ goto exit;
}
- ret = qemu_write_full(fd, desc, strlen(desc));
- if (ret != strlen(desc)) {
- ret = -errno;
- goto close_exit;
+ /* bdrv_pwrite write padding zeros to align to sector, we don't need that
+ * for description file */
+ if (desc_offset == 0) {
+ ret = bdrv_truncate(new_bs, desc_len);
+ if (ret < 0) {
+ error_setg(errp, "Could not truncate file");
+ }
}
- ret = 0;
-close_exit:
- qemu_close(fd);
exit:
+ if (new_bs) {
+ bdrv_unref(new_bs);
+ }
g_free(desc);
g_string_free(ext_desc_lines, true);
return ret;
@@ -1971,6 +2026,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
.bdrv_has_zero_init = vmdk_has_zero_init,
.bdrv_get_specific_info = vmdk_get_specific_info,
+ .bdrv_refresh_limits = vmdk_refresh_limits,
.create_options = vmdk_create_options,
};
diff --git a/block/vvfat.c b/block/vvfat.c
index 3ddaa0bcce..664941c560 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -266,8 +266,7 @@ typedef struct mbr_t {
} QEMU_PACKED mbr_t;
typedef struct direntry_t {
- uint8_t name[8];
- uint8_t extension[3];
+ uint8_t name[8 + 3];
uint8_t attributes;
uint8_t reserved[2];
uint16_t ctime;
@@ -518,11 +517,9 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
uint8_t chksum=0;
int i;
- for(i=0;i<11;i++) {
- unsigned char c;
-
- c = (i < 8) ? entry->name[i] : entry->extension[i-8];
- chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
+ for (i = 0; i < ARRAY_SIZE(entry->name); i++) {
+ chksum = (((chksum & 0xfe) >> 1) |
+ ((chksum & 0x01) ? 0x80 : 0)) + entry->name[i];
}
return chksum;
@@ -617,7 +614,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
if(is_dot) {
entry=array_get_next(&(s->directory));
- memset(entry->name,0x20,11);
+ memset(entry->name, 0x20, sizeof(entry->name));
memcpy(entry->name,filename,strlen(filename));
return entry;
}
@@ -632,12 +629,14 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
i = 8;
entry=array_get_next(&(s->directory));
- memset(entry->name,0x20,11);
+ memset(entry->name, 0x20, sizeof(entry->name));
memcpy(entry->name, filename, i);
- if(j > 0)
- for (i = 0; i < 3 && filename[j+1+i]; i++)
- entry->extension[i] = filename[j+1+i];
+ if (j > 0) {
+ for (i = 0; i < 3 && filename[j + 1 + i]; i++) {
+ entry->name[8 + i] = filename[j + 1 + i];
+ }
+ }
/* upcase & remove unwanted characters */
for(i=10;i>=0;i--) {
@@ -861,8 +860,7 @@ static int init_directories(BDRVVVFATState* s,
{
direntry_t* entry=array_get_next(&(s->directory));
entry->attributes=0x28; /* archive | volume label */
- memcpy(entry->name,"QEMU VVF",8);
- memcpy(entry->extension,"AT ",3);
+ memcpy(entry->name, "QEMU VVFAT ", sizeof(entry->name));
}
/* Now build FAT, and write back information into directory */
@@ -1085,7 +1083,7 @@ DLOG(if (stderr == NULL) {
setbuf(stderr, NULL);
})
- opts = qemu_opts_create_nofail(&runtime_opts);
+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -1591,17 +1589,20 @@ static int parse_short_name(BDRVVVFATState* s,
lfn->name[i] = direntry->name[i];
}
- for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
+ for (j = 2; j >= 0 && direntry->name[8 + j] == ' '; j--) {
+ }
if (j >= 0) {
lfn->name[i++] = '.';
lfn->name[i + j + 1] = '\0';
for (;j >= 0; j--) {
- if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
- return -2;
- else if (s->downcase_short_names)
- lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
- else
- lfn->name[i + j] = direntry->extension[j];
+ uint8_t c = direntry->name[8 + j];
+ if (c <= ' ' || c > 0x7f) {
+ return -2;
+ } else if (s->downcase_short_names) {
+ lfn->name[i + j] = qemu_tolower(c);
+ } else {
+ lfn->name[i + j] = c;
+ }
}
} else
lfn->name[i + j + 1] = '\0';
diff --git a/blockdev.c b/blockdev.c
index 44755e1a5d..36ceece9ff 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -307,12 +307,11 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
/* Takes the ownership of bs_opts */
-static DriveInfo *blockdev_init(QDict *bs_opts,
+static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
BlockInterfaceType type,
Error **errp)
{
const char *buf;
- const char *file = NULL;
const char *serial;
int ro = 0;
int bdrv_flags = 0;
@@ -354,7 +353,6 @@ static DriveInfo *blockdev_init(QDict *bs_opts,
ro = qemu_opt_get_bool(opts, "read-only", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
- file = qemu_opt_get(opts, "file");
serial = qemu_opt_get(opts, "serial");
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
@@ -599,6 +597,10 @@ QemuOptsList qemu_legacy_drive_opts = {
.name = "addr",
.type = QEMU_OPT_STRING,
.help = "pci address (virtio only)",
+ },{
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "file name",
},
/* Options that are passed on, but have special semantics with -drive */
@@ -629,6 +631,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
const char *devaddr;
bool read_only = false;
bool copy_on_read;
+ const char *filename;
Error *local_err = NULL;
/* Change legacy command line options into QMP ones */
@@ -682,7 +685,8 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
bs_opts = qdict_new();
qemu_opts_to_qdict(all_opts, bs_opts);
- legacy_opts = qemu_opts_create_nofail(&qemu_legacy_drive_opts);
+ legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
+ &error_abort);
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -853,7 +857,8 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
if (type == IF_VIRTIO) {
QemuOpts *devopts;
- devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
+ 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");
} else {
@@ -865,8 +870,10 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
}
}
+ filename = qemu_opt_get(legacy_opts, "file");
+
/* Actual block device init: Functionality shared with blockdev-add */
- dinfo = blockdev_init(bs_opts, type, &local_err);
+ dinfo = blockdev_init(filename, bs_opts, type, &local_err);
if (dinfo == NULL) {
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
@@ -940,14 +947,22 @@ static void blockdev_do_action(int kind, void *data, Error **errp)
qmp_transaction(&list, errp);
}
-void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
+void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
+ bool has_node_name, const char *node_name,
+ const char *snapshot_file,
+ bool has_snapshot_node_name,
+ const char *snapshot_node_name,
bool has_format, const char *format,
- bool has_mode, enum NewImageMode mode,
- Error **errp)
+ bool has_mode, NewImageMode mode, Error **errp)
{
BlockdevSnapshot snapshot = {
+ .has_device = has_device,
.device = (char *) device,
+ .has_node_name = has_node_name,
+ .node_name = (char *) node_name,
.snapshot_file = (char *) snapshot_file,
+ .has_snapshot_node_name = has_snapshot_node_name,
+ .snapshot_node_name = (char *) snapshot_node_name,
.has_format = has_format,
.format = (char *) format,
.has_mode = has_mode,
@@ -1185,8 +1200,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
{
BlockDriver *drv;
int flags, ret;
+ QDict *options = NULL;
Error *local_err = NULL;
+ bool has_device = false;
const char *device;
+ bool has_node_name = false;
+ const char *node_name;
+ bool has_snapshot_node_name = false;
+ const char *snapshot_node_name;
const char *new_image_file;
const char *format = "qcow2";
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
@@ -1197,7 +1218,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
/* get parameters */
g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
+ has_device = action->blockdev_snapshot_sync->has_device;
device = action->blockdev_snapshot_sync->device;
+ has_node_name = action->blockdev_snapshot_sync->has_node_name;
+ node_name = action->blockdev_snapshot_sync->node_name;
+ has_snapshot_node_name =
+ action->blockdev_snapshot_sync->has_snapshot_node_name;
+ snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name;
+
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
if (action->blockdev_snapshot_sync->has_format) {
format = action->blockdev_snapshot_sync->format;
@@ -1213,9 +1241,21 @@ static void external_snapshot_prepare(BlkTransactionState *common,
return;
}
- state->old_bs = bdrv_find(device);
- if (!state->old_bs) {
- error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
+ has_node_name ? node_name : NULL,
+ &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (has_node_name && !has_snapshot_node_name) {
+ error_setg(errp, "New snapshot node name missing");
+ return;
+ }
+
+ if (has_snapshot_node_name && bdrv_find_node(snapshot_node_name)) {
+ error_setg(errp, "New snapshot node name already existing");
return;
}
@@ -1236,7 +1276,7 @@ static void external_snapshot_prepare(BlkTransactionState *common,
}
}
- if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) {
+ if (!bdrv_is_first_non_filter(state->old_bs)) {
error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
return;
}
@@ -1255,15 +1295,23 @@ static void external_snapshot_prepare(BlkTransactionState *common,
}
}
+ if (has_snapshot_node_name) {
+ options = qdict_new();
+ qdict_put(options, "node-name",
+ qstring_from_str(snapshot_node_name));
+ }
+
/* We will manually add the backing_hd field to the bs later */
state->new_bs = bdrv_new("");
/* TODO Inherit bs->options or only take explicit options with an
* extended QMP command? */
- ret = bdrv_open(state->new_bs, new_image_file, NULL,
+ ret = bdrv_open(state->new_bs, new_image_file, options,
flags | BDRV_O_NO_BACKING, drv, &local_err);
if (ret != 0) {
error_propagate(errp, local_err);
}
+
+ QDECREF(options);
}
static void external_snapshot_commit(BlkTransactionState *common)
@@ -1474,14 +1522,19 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
eject_device(bs, force, errp);
}
-void qmp_block_passwd(const char *device, const char *password, Error **errp)
+void qmp_block_passwd(bool has_device, const char *device,
+ bool has_node_name, const char *node_name,
+ const char *password, Error **errp)
{
+ Error *local_err = NULL;
BlockDriverState *bs;
int err;
- bs = bdrv_find(device);
- if (!bs) {
- error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ bs = bdrv_lookup_bs(has_device ? device : NULL,
+ has_node_name ? node_name : NULL,
+ &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
return;
}
@@ -1523,7 +1576,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
}
void qmp_change_blockdev(const char *device, const char *filename,
- bool has_format, const char *format, Error **errp)
+ const char *format, Error **errp)
{
BlockDriverState *bs;
BlockDriver *drv = NULL;
@@ -1671,14 +1724,24 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
-void qmp_block_resize(const char *device, int64_t size, Error **errp)
+void qmp_block_resize(bool has_device, const char *device,
+ bool has_node_name, const char *node_name,
+ int64_t size, Error **errp)
{
+ Error *local_err = NULL;
BlockDriverState *bs;
int ret;
- bs = bdrv_find(device);
- if (!bs) {
- error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ bs = bdrv_lookup_bs(has_device ? device : NULL,
+ has_node_name ? node_name : NULL,
+ &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (!bdrv_is_first_non_filter(bs)) {
+ error_set(errp, QERR_FEATURE_DISABLED, "resize");
return;
}
@@ -1820,8 +1883,13 @@ void qmp_block_commit(const char *device,
return;
}
- commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
- &local_err);
+ if (top_bs == bs) {
+ commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
+ bs, &local_err);
+ } else {
+ commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
+ &local_err);
+ }
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
@@ -1940,6 +2008,11 @@ void qmp_drive_backup(const char *device, const char *target,
}
}
+BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
+{
+ return bdrv_named_nodes_list();
+}
+
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
void qmp_drive_mirror(const char *device, const char *target,
@@ -2203,7 +2276,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
qdict_flatten(qdict);
- blockdev_init(qdict, IF_NONE, &local_err);
+ blockdev_init(NULL, qdict, IF_NONE, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto fail;
@@ -2244,10 +2317,6 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_BOOL,
.help = "enable/disable snapshot mode",
},{
- .name = "file",
- .type = QEMU_OPT_STRING,
- .help = "disk image",
- },{
.name = "discard",
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
diff --git a/configure b/configure
index 5d82283a14..b472694cb2 100755
--- a/configure
+++ b/configure
@@ -256,6 +256,7 @@ coroutine_pool=""
seccomp=""
glusterfs=""
glusterfs_discard="no"
+glusterfs_zerofill="no"
virtio_blk_data_plane=""
gtk=""
gtkabi="2.0"
@@ -1004,6 +1005,25 @@ for opt do
esac
done
+if ! has $python; then
+ error_exit "Python not found. Use --python=/path/to/python"
+fi
+
+# Note that if the Python conditional here evaluates True we will exit
+# with status 1 which is a shell 'false' value.
+if ! $python -c 'import sys; sys.exit(sys.version_info < (2,4) or sys.version_info >= (3,))'; then
+ error_exit "Cannot use '$python', Python 2.4 or later is required." \
+ "Note that Python 3 or later is not yet supported." \
+ "Use --python=/path/to/python to specify a supported Python."
+fi
+
+# The -B switch was added in Python 2.6.
+# If it is supplied, compiled files are not written.
+# Use it for Python versions which support it.
+if $python -B -c 'import sys; sys.exit(0)' 2>/dev/null; then
+ python="$python -B"
+fi
+
case "$cpu" in
ppc)
CPU_CFLAGS="-m32"
@@ -1074,169 +1094,169 @@ cat << EOF
Usage: configure [options]
Options: [defaults in brackets after descriptions]
+Standard options:
+ --help print this message
+ --prefix=PREFIX install in PREFIX [$prefix]
+ --interp-prefix=PREFIX where to find shared libraries, etc.
+ use %M for cpu name [$interp_prefix]
+ --target-list=LIST set target list (default: build everything)
+$(echo Available targets: $default_target_list | \
+ fold -s -w 53 | sed -e 's/^/ /')
+
+Advanced options (experts only):
+ --source-path=PATH path of source code [$source_path]
+ --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]
+ --cc=CC use C compiler CC [$cc]
+ --iasl=IASL use ACPI compiler IASL [$iasl]
+ --host-cc=CC use C compiler CC [$host_cc] for code run at
+ build time
+ --cxx=CXX use C++ compiler CXX [$cxx]
+ --objcc=OBJCC use Objective-C compiler OBJCC [$objcc]
+ --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
+ --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
+ --make=MAKE use specified make [$make]
+ --install=INSTALL use specified install [$install]
+ --python=PYTHON use specified python [$python]
+ --smbd=SMBD use specified smbd [$smbd]
+ --static enable static build [$static]
+ --mandir=PATH install man pages in PATH
+ --datadir=PATH install firmware in PATH$confsuffix
+ --docdir=PATH install documentation in PATH$confsuffix
+ --bindir=PATH install binaries in PATH
+ --libdir=PATH install libraries in PATH
+ --sysconfdir=PATH install config in PATH$confsuffix
+ --localstatedir=PATH install local state in PATH (set at runtime on win32)
+ --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]
+ --enable-debug-tcg enable TCG debugging
+ --disable-debug-tcg disable TCG debugging (default)
+ --enable-debug-info enable debugging information (default)
+ --disable-debug-info disable debugging information
+ --enable-debug enable common debug build options
+ --enable-sparse enable sparse checker
+ --disable-sparse disable sparse checker (default)
+ --disable-strip disable stripping binaries
+ --disable-werror disable compilation abort on warning
+ --disable-sdl disable SDL
+ --enable-sdl enable SDL
+ --disable-gtk disable gtk UI
+ --enable-gtk enable gtk UI
+ --disable-virtfs disable VirtFS
+ --enable-virtfs enable VirtFS
+ --disable-vnc disable VNC
+ --enable-vnc enable VNC
+ --disable-cocoa disable Cocoa (Mac OS X only)
+ --enable-cocoa enable Cocoa (default on Mac OS X)
+ --audio-drv-list=LIST set audio drivers list:
+ Available drivers: $audio_possible_drivers
+ --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L
+ --block-drv-rw-whitelist=L
+ set block driver read-write whitelist
+ (affects only QEMU, not qemu-img)
+ --block-drv-ro-whitelist=L
+ set block driver read-only whitelist
+ (affects only QEMU, not qemu-img)
+ --disable-xen disable xen backend driver support
+ --enable-xen enable xen backend driver support
+ --disable-xen-pci-passthrough
+ --enable-xen-pci-passthrough
+ --disable-brlapi disable BrlAPI
+ --enable-brlapi enable BrlAPI
+ --disable-vnc-tls disable TLS encryption for VNC server
+ --enable-vnc-tls enable TLS encryption for VNC server
+ --disable-vnc-sasl disable SASL encryption for VNC server
+ --enable-vnc-sasl enable SASL encryption for VNC server
+ --disable-vnc-jpeg disable JPEG lossy compression for VNC server
+ --enable-vnc-jpeg enable JPEG lossy compression for VNC server
+ --disable-vnc-png disable PNG compression for VNC server (default)
+ --enable-vnc-png enable PNG compression for VNC server
+ --disable-vnc-ws disable Websockets support for VNC server
+ --enable-vnc-ws enable Websockets support for VNC server
+ --disable-curses disable curses output
+ --enable-curses enable curses output
+ --disable-curl disable curl connectivity
+ --enable-curl enable curl connectivity
+ --disable-fdt disable fdt device tree
+ --enable-fdt enable fdt device tree
+ --disable-bluez disable bluez stack connectivity
+ --enable-bluez enable bluez stack connectivity
+ --disable-slirp disable SLIRP userspace network connectivity
+ --disable-kvm disable KVM acceleration support
+ --enable-kvm enable KVM acceleration support
+ --disable-rdma disable RDMA-based migration support
+ --enable-rdma enable RDMA-based migration support
+ --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)
+ --enable-system enable all system emulation targets
+ --disable-system disable all system emulation targets
+ --enable-user enable supported user emulation targets
+ --disable-user disable all user emulation targets
+ --enable-linux-user enable all linux usermode emulation targets
+ --disable-linux-user disable all linux usermode emulation targets
+ --enable-bsd-user enable all BSD usermode emulation targets
+ --disable-bsd-user disable all BSD usermode emulation targets
+ --enable-guest-base enable GUEST_BASE support for usermode
+ emulation targets
+ --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
+ --enable-uname-release=R Return R for uname -r in usermode emulation
+ --cpu=CPU Build for host CPU [$cpu]
+ --disable-uuid disable uuid support
+ --enable-uuid enable uuid support
+ --disable-vde disable support for vde network
+ --enable-vde enable support for vde network
+ --disable-netmap disable support for netmap network
+ --enable-netmap enable support for netmap network
+ --disable-linux-aio disable Linux AIO support
+ --enable-linux-aio enable Linux AIO support
+ --disable-cap-ng disable libcap-ng support
+ --enable-cap-ng enable libcap-ng support
+ --disable-attr disables attr and xattr support
+ --enable-attr enable attr and xattr support
+ --disable-blobs disable installing provided firmware blobs
+ --enable-docs enable documentation build
+ --disable-docs disable documentation build
+ --disable-vhost-net disable vhost-net acceleration support
+ --enable-vhost-net enable vhost-net acceleration support
+ --enable-trace-backend=B Set trace backend
+ Available backends: $($python $source_path/scripts/tracetool.py --list-backends)
+ --with-trace-file=NAME Full PATH,NAME of file to store traces
+ Default:trace-<pid>
+ --disable-spice disable spice
+ --enable-spice enable spice
+ --enable-rbd enable building the rados block device (rbd)
+ --disable-libiscsi disable iscsi support
+ --enable-libiscsi enable iscsi support
+ --disable-smartcard-nss disable smartcard nss support
+ --enable-smartcard-nss enable smartcard nss support
+ --disable-libusb disable libusb (for usb passthrough)
+ --enable-libusb enable libusb (for usb passthrough)
+ --disable-usb-redir disable usb network redirection support
+ --enable-usb-redir enable usb network redirection support
+ --disable-guest-agent disable building of the QEMU Guest Agent
+ --enable-guest-agent enable building of the QEMU Guest Agent
+ --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent
+ --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb)
+ --disable-seccomp disable seccomp support
+ --enable-seccomp enables seccomp support
+ --with-coroutine=BACKEND coroutine backend. Supported options:
+ gthread, ucontext, sigaltstack, windows
+ --disable-coroutine-pool disable coroutine freelist (worse performance)
+ --enable-coroutine-pool enable coroutine freelist (better performance)
+ --enable-glusterfs enable GlusterFS backend
+ --disable-glusterfs disable GlusterFS backend
+ --enable-gcov enable test coverage analysis with gcov
+ --gcov=GCOV use specified gcov [$gcov_tool]
+ --enable-tpm enable TPM support
+ --disable-libssh2 disable ssh block device support
+ --enable-libssh2 enable ssh block device support
+ --disable-vhdx disables support for the Microsoft VHDX image format
+ --enable-vhdx enable support for the Microsoft VHDX image format
+
+NOTE: The object files are built at the place where configure is launched
EOF
-echo "Standard options:"
-echo " --help print this message"
-echo " --prefix=PREFIX install in PREFIX [$prefix]"
-echo " --interp-prefix=PREFIX where to find shared libraries, etc."
-echo " use %M for cpu name [$interp_prefix]"
-echo " --target-list=LIST set target list (default: build everything)"
-echo "Available targets: $default_target_list" | \
- fold -s -w 53 | sed -e 's/^/ /'
-echo ""
-echo "Advanced options (experts only):"
-echo " --source-path=PATH path of source code [$source_path]"
-echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
-echo " --cc=CC use C compiler CC [$cc]"
-echo " --iasl=IASL use ACPI compiler IASL [$iasl]"
-echo " --host-cc=CC use C compiler CC [$host_cc] for code run at"
-echo " build time"
-echo " --cxx=CXX use C++ compiler CXX [$cxx]"
-echo " --objcc=OBJCC use Objective-C compiler OBJCC [$objcc]"
-echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS"
-echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS"
-echo " --make=MAKE use specified make [$make]"
-echo " --install=INSTALL use specified install [$install]"
-echo " --python=PYTHON use specified python [$python]"
-echo " --smbd=SMBD use specified smbd [$smbd]"
-echo " --static enable static build [$static]"
-echo " --mandir=PATH install man pages in PATH"
-echo " --datadir=PATH install firmware in PATH$confsuffix"
-echo " --docdir=PATH install documentation in PATH$confsuffix"
-echo " --bindir=PATH install binaries in PATH"
-echo " --libdir=PATH install libraries in PATH"
-echo " --sysconfdir=PATH install config in PATH$confsuffix"
-echo " --localstatedir=PATH install local state in PATH (set at runtime on win32)"
-echo " --with-confsuffix=SUFFIX suffix for QEMU data inside datadir and sysconfdir [$confsuffix]"
-echo " --enable-debug-tcg enable TCG debugging"
-echo " --disable-debug-tcg disable TCG debugging (default)"
-echo " --enable-debug-info enable debugging information (default)"
-echo " --disable-debug-info disable debugging information"
-echo " --enable-debug enable common debug build options"
-echo " --enable-sparse enable sparse checker"
-echo " --disable-sparse disable sparse checker (default)"
-echo " --disable-strip disable stripping binaries"
-echo " --disable-werror disable compilation abort on warning"
-echo " --disable-sdl disable SDL"
-echo " --enable-sdl enable SDL"
-echo " --disable-gtk disable gtk UI"
-echo " --enable-gtk enable gtk UI"
-echo " --disable-virtfs disable VirtFS"
-echo " --enable-virtfs enable VirtFS"
-echo " --disable-vnc disable VNC"
-echo " --enable-vnc enable VNC"
-echo " --disable-cocoa disable Cocoa (Mac OS X only)"
-echo " --enable-cocoa enable Cocoa (default on Mac OS X)"
-echo " --audio-drv-list=LIST set audio drivers list:"
-echo " Available drivers: $audio_possible_drivers"
-echo " --block-drv-whitelist=L Same as --block-drv-rw-whitelist=L"
-echo " --block-drv-rw-whitelist=L"
-echo " set block driver read-write whitelist"
-echo " (affects only QEMU, not qemu-img)"
-echo " --block-drv-ro-whitelist=L"
-echo " set block driver read-only whitelist"
-echo " (affects only QEMU, not qemu-img)"
-echo " --disable-xen disable xen backend driver support"
-echo " --enable-xen enable xen backend driver support"
-echo " --disable-xen-pci-passthrough"
-echo " --enable-xen-pci-passthrough"
-echo " --disable-brlapi disable BrlAPI"
-echo " --enable-brlapi enable BrlAPI"
-echo " --disable-vnc-tls disable TLS encryption for VNC server"
-echo " --enable-vnc-tls enable TLS encryption for VNC server"
-echo " --disable-vnc-sasl disable SASL encryption for VNC server"
-echo " --enable-vnc-sasl enable SASL encryption for VNC server"
-echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
-echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
-echo " --disable-vnc-png disable PNG compression for VNC server (default)"
-echo " --enable-vnc-png enable PNG compression for VNC server"
-echo " --disable-vnc-ws disable Websockets support for VNC server"
-echo " --enable-vnc-ws enable Websockets support for VNC server"
-echo " --disable-curses disable curses output"
-echo " --enable-curses enable curses output"
-echo " --disable-curl disable curl connectivity"
-echo " --enable-curl enable curl connectivity"
-echo " --disable-fdt disable fdt device tree"
-echo " --enable-fdt enable fdt device tree"
-echo " --disable-bluez disable bluez stack connectivity"
-echo " --enable-bluez enable bluez stack connectivity"
-echo " --disable-slirp disable SLIRP userspace network connectivity"
-echo " --disable-kvm disable KVM acceleration support"
-echo " --enable-kvm enable KVM acceleration support"
-echo " --disable-rdma disable RDMA-based migration support"
-echo " --enable-rdma enable RDMA-based migration support"
-echo " --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)"
-echo " --enable-system enable all system emulation targets"
-echo " --disable-system disable all system emulation targets"
-echo " --enable-user enable supported user emulation targets"
-echo " --disable-user disable all user emulation targets"
-echo " --enable-linux-user enable all linux usermode emulation targets"
-echo " --disable-linux-user disable all linux usermode emulation targets"
-echo " --enable-bsd-user enable all BSD usermode emulation targets"
-echo " --disable-bsd-user disable all BSD usermode emulation targets"
-echo " --enable-guest-base enable GUEST_BASE support for usermode"
-echo " emulation targets"
-echo " --disable-guest-base disable GUEST_BASE support"
-echo " --enable-pie build Position Independent Executables"
-echo " --disable-pie do not build Position Independent Executables"
-echo " --fmod-lib path to FMOD library"
-echo " --fmod-inc path to FMOD includes"
-echo " --oss-lib path to OSS library"
-echo " --enable-uname-release=R Return R for uname -r in usermode emulation"
-echo " --cpu=CPU Build for host CPU [$cpu]"
-echo " --disable-uuid disable uuid support"
-echo " --enable-uuid enable uuid support"
-echo " --disable-vde disable support for vde network"
-echo " --enable-vde enable support for vde network"
-echo " --disable-netmap disable support for netmap network"
-echo " --enable-netmap enable support for netmap network"
-echo " --disable-linux-aio disable Linux AIO support"
-echo " --enable-linux-aio enable Linux AIO support"
-echo " --disable-cap-ng disable libcap-ng support"
-echo " --enable-cap-ng enable libcap-ng support"
-echo " --disable-attr disables attr and xattr support"
-echo " --enable-attr enable attr and xattr support"
-echo " --disable-blobs disable installing provided firmware blobs"
-echo " --enable-docs enable documentation build"
-echo " --disable-docs disable documentation build"
-echo " --disable-vhost-net disable vhost-net acceleration support"
-echo " --enable-vhost-net enable vhost-net acceleration support"
-echo " --enable-trace-backend=B Set trace backend"
-echo " Available backends:" $($python "$source_path"/scripts/tracetool.py --list-backends)
-echo " --with-trace-file=NAME Full PATH,NAME of file to store traces"
-echo " Default:trace-<pid>"
-echo " --disable-spice disable spice"
-echo " --enable-spice enable spice"
-echo " --enable-rbd enable building the rados block device (rbd)"
-echo " --disable-libiscsi disable iscsi support"
-echo " --enable-libiscsi enable iscsi support"
-echo " --disable-smartcard-nss disable smartcard nss support"
-echo " --enable-smartcard-nss enable smartcard nss support"
-echo " --disable-libusb disable libusb (for usb passthrough)"
-echo " --enable-libusb enable libusb (for usb passthrough)"
-echo " --disable-usb-redir disable usb network redirection support"
-echo " --enable-usb-redir enable usb network redirection support"
-echo " --disable-guest-agent disable building of the QEMU Guest Agent"
-echo " --enable-guest-agent enable building of the QEMU Guest Agent"
-echo " --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent"
-echo " --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb)"
-echo " --disable-seccomp disable seccomp support"
-echo " --enable-seccomp enables seccomp support"
-echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
-echo " gthread, ucontext, sigaltstack, windows"
-echo " --disable-coroutine-pool disable coroutine freelist (worse performance)"
-echo " --enable-coroutine-pool enable coroutine freelist (better performance)"
-echo " --enable-glusterfs enable GlusterFS backend"
-echo " --disable-glusterfs disable GlusterFS backend"
-echo " --enable-gcov enable test coverage analysis with gcov"
-echo " --gcov=GCOV use specified gcov [$gcov_tool]"
-echo " --enable-tpm enable TPM support"
-echo " --disable-libssh2 disable ssh block device support"
-echo " --enable-libssh2 enable ssh block device support"
-echo " --disable-vhdx disables support for the Microsoft VHDX image format"
-echo " --enable-vhdx enable support for the Microsoft VHDX image format"
-echo ""
-echo "NOTE: The object files are built at the place where configure is launched"
exit 1
fi
@@ -1419,25 +1439,6 @@ if test "$solaris" = "yes" ; then
fi
fi
-if ! has $python; then
- error_exit "Python not found. Use --python=/path/to/python"
-fi
-
-# Note that if the Python conditional here evaluates True we will exit
-# with status 1 which is a shell 'false' value.
-if ! $python -c 'import sys; sys.exit(sys.version_info < (2,4) or sys.version_info >= (3,))'; then
- error_exit "Cannot use '$python', Python 2.4 or later is required." \
- "Note that Python 3 or later is not yet supported." \
- "Use --python=/path/to/python to specify a supported Python."
-fi
-
-# The -B switch was added in Python 2.6.
-# If it is supplied, compiled files are not written.
-# Use it for Python versions which support it.
-if $python -B -c 'import sys; sys.exit(0)' 2>/dev/null; then
- python="$python -B"
-fi
-
if test -z "${target_list+xxx}" ; then
target_list="$default_target_list"
else
@@ -2701,6 +2702,9 @@ if test "$glusterfs" != "no" ; then
if $pkg_config --atleast-version=5 glusterfs-api; then
glusterfs_discard="yes"
fi
+ if $pkg_config --atleast-version=6 glusterfs-api; then
+ glusterfs_zerofill="yes"
+ fi
else
if test "$glusterfs" = "yes" ; then
feature_not_found "GlusterFS backend support"
@@ -3053,13 +3057,13 @@ fi
##########################################
# Do we have libiscsi
-# We check for iscsi_unmap_sync() to make sure we have a
-# recent enough version of libiscsi.
+# We check for iscsi_write16_sync() to make sure we have a
+# at least version 1.4.0 of libiscsi.
if test "$libiscsi" != "no" ; then
cat > $TMPC << EOF
#include <stdio.h>
#include <iscsi/iscsi.h>
-int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; }
+int main(void) { iscsi_write16_sync(NULL,0,0,NULL,0,0,0,0,0,0,0); return 0; }
EOF
if $pkg_config --atleast-version=1.7.0 libiscsi; then
libiscsi="yes"
@@ -3078,6 +3082,21 @@ EOF
fi
fi
+# We also need to know the API version because there was an
+# API change from 1.4.0 to 1.5.0.
+if test "$libiscsi" = "yes"; then
+ cat >$TMPC <<EOF
+#include <iscsi/iscsi.h>
+int main(void)
+{
+ iscsi_read10_task(0, 0, 0, 0, 0, 0, 0);
+ return 0;
+}
+EOF
+ if compile_prog "" "-liscsi"; then
+ libiscsi_version="1.4.0"
+ fi
+fi
##########################################
# Do we need libm
@@ -3805,7 +3824,11 @@ echo "nss used $smartcard_nss"
echo "libusb $libusb"
echo "usb net redir $usb_redir"
echo "GLX support $glx"
+if test "$libiscsi_version" = "1.4.0"; then
+echo "libiscsi support $libiscsi (1.4.0)"
+else
echo "libiscsi support $libiscsi"
+fi
echo "build guest agent $guest_agent"
echo "QGA VSS support $guest_agent_with_vss"
echo "seccomp support $seccomp"
@@ -4137,6 +4160,9 @@ fi
if test "$libiscsi" = "yes" ; then
echo "CONFIG_LIBISCSI=y" >> $config_host_mak
+ if test "$libiscsi_version" = "1.4.0"; then
+ echo "CONFIG_LIBISCSI_1_4=y" >> $config_host_mak
+ fi
fi
if test "$seccomp" = "yes"; then
@@ -4207,6 +4233,10 @@ if test "$glusterfs_discard" = "yes" ; then
echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
fi
+if test "$glusterfs_zerofill" = "yes" ; then
+ echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
+fi
+
if test "$libssh2" = "yes" ; then
echo "CONFIG_LIBSSH2=y" >> $config_host_mak
fi
@@ -4438,7 +4468,7 @@ case "$target_name" in
aarch64)
TARGET_BASE_ARCH=arm
bflt="yes"
- gdb_xml_files="aarch64-core.xml"
+ gdb_xml_files="aarch64-core.xml aarch64-fpu.xml"
;;
cris)
;;
@@ -4550,7 +4580,7 @@ case "$target_name" in
*)
esac
case "$target_name" in
- arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
+ aarch64|arm|i386|x86_64|ppcemb|ppc|ppc64|s390x)
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
\( "$target_name" = "$cpu" -o \
diff --git a/cpu-exec.c b/cpu-exec.c
index 30cfa2a63a..a6c01f4193 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -53,7 +53,25 @@ void cpu_resume_from_signal(CPUArchState *env, void *puc)
static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
{
CPUArchState *env = cpu->env_ptr;
- uintptr_t next_tb = tcg_qemu_tb_exec(env, tb_ptr);
+ uintptr_t next_tb;
+
+#if defined(DEBUG_DISAS)
+ if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
+#if defined(TARGET_I386)
+ log_cpu_state(cpu, CPU_DUMP_CCOP);
+#elif defined(TARGET_M68K)
+ /* ??? Should not modify env state for dumping. */
+ cpu_m68k_flush_flags(env, env->cc_op);
+ env->cc_op = CC_OP_FLAGS;
+ env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4);
+ log_cpu_state(cpu, 0);
+#else
+ log_cpu_state(cpu, 0);
+#endif
+ }
+#endif /* DEBUG_DISAS */
+
+ next_tb = tcg_qemu_tb_exec(env, tb_ptr);
if ((next_tb & TB_EXIT_MASK) > TB_EXIT_IDX1) {
/* We didn't start executing this TB (eg because the instruction
* counter hit zero); we must restore the guest PC to the address
@@ -206,6 +224,9 @@ int cpu_exec(CPUArchState *env)
(defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
CPUClass *cc = CPU_GET_CLASS(cpu);
#endif
+#ifdef TARGET_I386
+ X86CPU *x86_cpu = X86_CPU(cpu);
+#endif
int ret, interrupt_request;
TranslationBlock *tb;
uint8_t *tc_ptr;
@@ -320,24 +341,24 @@ int cpu_exec(CPUArchState *env)
#if !defined(CONFIG_USER_ONLY)
if (interrupt_request & CPU_INTERRUPT_POLL) {
cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(x86_cpu->apic_state);
}
#endif
if (interrupt_request & CPU_INTERRUPT_INIT) {
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT,
0);
- do_cpu_init(x86_env_get_cpu(env));
+ do_cpu_init(x86_cpu);
env->exception_index = EXCP_HALTED;
cpu_loop_exit(env);
} else if (interrupt_request & CPU_INTERRUPT_SIPI) {
- do_cpu_sipi(x86_env_get_cpu(env));
+ do_cpu_sipi(x86_cpu);
} else if (env->hflags2 & HF2_GIF_MASK) {
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
!(env->hflags & HF_SMM_MASK)) {
cpu_svm_check_intercept_param(env, SVM_EXIT_SMI,
0);
cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
- do_smm_enter(x86_env_get_cpu(env));
+ do_smm_enter(x86_cpu);
next_tb = 0;
} else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
!(env->hflags2 & HF2_NMI_MASK)) {
@@ -579,22 +600,6 @@ int cpu_exec(CPUArchState *env)
env->exception_index = EXCP_INTERRUPT;
cpu_loop_exit(env);
}
-#if defined(DEBUG_DISAS)
- if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
- /* restore flags in standard format */
-#if defined(TARGET_I386)
- log_cpu_state(cpu, CPU_DUMP_CCOP);
-#elif defined(TARGET_M68K)
- cpu_m68k_flush_flags(env, env->cc_op);
- env->cc_op = CC_OP_FLAGS;
- env->sr = (env->sr & 0xffe0)
- | env->cc_dest | (env->cc_x << 4);
- log_cpu_state(cpu, 0);
-#else
- log_cpu_state(cpu, 0);
-#endif
- }
-#endif /* DEBUG_DISAS */
spin_lock(&tcg_ctx.tb_ctx.tb_lock);
tb = tb_find_fast(env);
/* Note: we do it here to avoid a gcc bug on Mac OS X when
@@ -685,6 +690,9 @@ int cpu_exec(CPUArchState *env)
(defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X)))
cc = CPU_GET_CLASS(cpu);
#endif
+#ifdef TARGET_I386
+ x86_cpu = X86_CPU(cpu);
+#endif
}
} /* for(;;) */
diff --git a/cpus.c b/cpus.c
index 01d128d7af..ca4c59fe0b 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1458,12 +1458,11 @@ void qmp_inject_nmi(Error **errp)
CPU_FOREACH(cs) {
X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
- if (!env->apic_state) {
+ if (!cpu->apic_state) {
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
} else {
- apic_deliver_nmi(env->apic_state);
+ apic_deliver_nmi(cpu->apic_state);
}
}
#elif defined(TARGET_S390X)
diff --git a/cputlb.c b/cputlb.c
index fff0afbd4a..b533f3f372 100644
--- a/cputlb.c
+++ b/cputlb.c
@@ -26,6 +26,7 @@
#include "exec/cputlb.h"
#include "exec/memory-internal.h"
+#include "exec/ram_addr.h"
//#define DEBUG_TLB
//#define DEBUG_TLB_CHECK
@@ -33,13 +34,6 @@
/* statistics */
int tlb_flush_count;
-static const CPUTLBEntry s_cputlb_empty_entry = {
- .addr_read = -1,
- .addr_write = -1,
- .addr_code = -1,
- .addend = -1,
-};
-
/* NOTE:
* If flush_global is true (the usual case), flush all tlb entries.
* If flush_global is false, flush (at least) all tlb entries not
@@ -55,7 +49,6 @@ static const CPUTLBEntry s_cputlb_empty_entry = {
void tlb_flush(CPUArchState *env, int flush_global)
{
CPUState *cpu = ENV_GET_CPU(env);
- int i;
#if defined(DEBUG_TLB)
printf("tlb_flush:\n");
@@ -64,15 +57,8 @@ void tlb_flush(CPUArchState *env, int flush_global)
links while we are modifying them */
cpu->current_tb = NULL;
- for (i = 0; i < CPU_TLB_SIZE; i++) {
- int mmu_idx;
-
- for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
- env->tlb_table[mmu_idx][i] = s_cputlb_empty_entry;
- }
- }
-
- memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+ memset(env->tlb_table, -1, sizeof(env->tlb_table));
+ memset(env->tb_jmp_cache, 0, sizeof(env->tb_jmp_cache));
env->tlb_flush_addr = -1;
env->tlb_flush_mask = 0;
@@ -87,7 +73,7 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
addr == (tlb_entry->addr_code &
(TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
- *tlb_entry = s_cputlb_empty_entry;
+ memset(tlb_entry, -1, sizeof(*tlb_entry));
}
}
@@ -127,9 +113,8 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr)
can be detected */
void tlb_protect_code(ram_addr_t ram_addr)
{
- cpu_physical_memory_reset_dirty(ram_addr,
- ram_addr + TARGET_PAGE_SIZE,
- CODE_DIRTY_FLAG);
+ cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE,
+ DIRTY_MEMORY_CODE);
}
/* update the TLB so that writes in physical page 'phys_addr' are no longer
@@ -137,7 +122,7 @@ void tlb_protect_code(ram_addr_t ram_addr)
void tlb_unprotect_code_phys(CPUArchState *env, ram_addr_t ram_addr,
target_ulong vaddr)
{
- cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG);
+ cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
}
static bool tlb_is_dirty_ram(CPUTLBEntry *tlbe)
@@ -299,7 +284,8 @@ void tlb_set_page(CPUArchState *env, target_ulong vaddr,
/* Write access calls the I/O callback. */
te->addr_write = address | TLB_MMIO;
} else if (memory_region_is_ram(section->mr)
- && !cpu_physical_memory_is_dirty(section->mr->ram_addr + xlat)) {
+ && cpu_physical_memory_is_clean(section->mr->ram_addr
+ + xlat)) {
te->addr_write = address | TLB_NOTDIRTY;
} else {
te->addr_write = address;
diff --git a/default-configs/aarch64-linux-user.mak b/default-configs/aarch64-linux-user.mak
new file mode 100644
index 0000000000..3df7de5b8f
--- /dev/null
+++ b/default-configs/aarch64-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for aarch64-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak
new file mode 100644
index 0000000000..6d3b5c7a46
--- /dev/null
+++ b/default-configs/aarch64-softmmu.mak
@@ -0,0 +1,6 @@
+# Default configuration for aarch64-softmmu
+
+# We support all the 32 bit boards so need all their config
+include arm-softmmu.mak
+
+# Currently no 64-bit specific config requirements
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index e48f102af6..ce1d620842 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -64,6 +64,7 @@ CONFIG_XILINX_SPIPS=y
CONFIG_ARM11SCU=y
CONFIG_A9SCU=y
+CONFIG_DIGIC=y
CONFIG_MARVELL_88W8618=y
CONFIG_OMAP=y
CONFIG_TSC210X=y
@@ -82,3 +83,7 @@ CONFIG_VERSATILE_I2C=y
CONFIG_SDHCI=y
CONFIG_INTEGRATOR_DEBUG=y
+
+CONFIG_ALLWINNER_A10_PIT=y
+CONFIG_ALLWINNER_A10_PIC=y
+CONFIG_ALLWINNER_A10=y
diff --git a/device_tree.c b/device_tree.c
index 391da8c45e..ca83504819 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -131,12 +131,12 @@ static int findnode_nofail(void *fdt, const char *node_path)
return offset;
}
-int qemu_devtree_setprop(void *fdt, const char *node_path,
- const char *property, const void *val_array, int size)
+int qemu_fdt_setprop(void *fdt, const char *node_path,
+ const char *property, const void *val, int size)
{
int r;
- r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size);
+ r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
if (r < 0) {
fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path,
property, fdt_strerror(r));
@@ -146,8 +146,8 @@ int qemu_devtree_setprop(void *fdt, const char *node_path,
return r;
}
-int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
- const char *property, uint32_t val)
+int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val)
{
int r;
@@ -161,15 +161,15 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
return r;
}
-int qemu_devtree_setprop_u64(void *fdt, const char *node_path,
- const char *property, uint64_t val)
+int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
+ const char *property, uint64_t val)
{
val = cpu_to_be64(val);
- return qemu_devtree_setprop(fdt, node_path, property, &val, sizeof(val));
+ return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
}
-int qemu_devtree_setprop_string(void *fdt, const char *node_path,
- const char *property, const char *string)
+int qemu_fdt_setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string)
{
int r;
@@ -183,8 +183,8 @@ int qemu_devtree_setprop_string(void *fdt, const char *node_path,
return r;
}
-const void *qemu_devtree_getprop(void *fdt, const char *node_path,
- const char *property, int *lenp)
+const void *qemu_fdt_getprop(void *fdt, const char *node_path,
+ const char *property, int *lenp)
{
int len;
const void *r;
@@ -200,11 +200,11 @@ const void *qemu_devtree_getprop(void *fdt, const char *node_path,
return r;
}
-uint32_t qemu_devtree_getprop_cell(void *fdt, const char *node_path,
- const char *property)
+uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
+ const char *property)
{
int len;
- const uint32_t *p = qemu_devtree_getprop(fdt, node_path, property, &len);
+ const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len);
if (len != 4) {
fprintf(stderr, "%s: %s/%s not 4 bytes long (not a cell?)\n",
__func__, node_path, property);
@@ -213,7 +213,7 @@ uint32_t qemu_devtree_getprop_cell(void *fdt, const char *node_path,
return be32_to_cpu(*p);
}
-uint32_t qemu_devtree_get_phandle(void *fdt, const char *path)
+uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
{
uint32_t r;
@@ -227,15 +227,15 @@ uint32_t qemu_devtree_get_phandle(void *fdt, const char *path)
return r;
}
-int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
- const char *property,
- const char *target_node_path)
+int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
+ const char *property,
+ const char *target_node_path)
{
- uint32_t phandle = qemu_devtree_get_phandle(fdt, target_node_path);
- return qemu_devtree_setprop_cell(fdt, node_path, property, phandle);
+ uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
+ return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
}
-uint32_t qemu_devtree_alloc_phandle(void *fdt)
+uint32_t qemu_fdt_alloc_phandle(void *fdt)
{
static int phandle = 0x0;
@@ -259,7 +259,7 @@ uint32_t qemu_devtree_alloc_phandle(void *fdt)
return phandle++;
}
-int qemu_devtree_nop_node(void *fdt, const char *node_path)
+int qemu_fdt_nop_node(void *fdt, const char *node_path)
{
int r;
@@ -273,7 +273,7 @@ int qemu_devtree_nop_node(void *fdt, const char *node_path)
return r;
}
-int qemu_devtree_add_subnode(void *fdt, const char *name)
+int qemu_fdt_add_subnode(void *fdt, const char *name)
{
char *dupname = g_strdup(name);
char *basename = strrchr(dupname, '/');
@@ -303,7 +303,7 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
return retval;
}
-void qemu_devtree_dumpdtb(void *fdt, int size)
+void qemu_fdt_dumpdtb(void *fdt, int size)
{
const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
@@ -313,11 +313,11 @@ void qemu_devtree_dumpdtb(void *fdt, int size)
}
}
-int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
- const char *node_path,
- const char *property,
- int numvalues,
- uint64_t *values)
+int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
+ const char *node_path,
+ const char *property,
+ int numvalues,
+ uint64_t *values)
{
uint32_t *propcells;
uint64_t value;
@@ -342,6 +342,6 @@ int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
propcells[cellnum++] = cpu_to_be32(value);
}
- return qemu_devtree_setprop(fdt, node_path, property, propcells,
- cellnum * sizeof(uint32_t));
+ return qemu_fdt_setprop(fdt, node_path, property, propcells,
+ cellnum * sizeof(uint32_t));
}
diff --git a/exec.c b/exec.c
index 52d451b547..2435d9ecd9 100644
--- a/exec.c
+++ b/exec.c
@@ -50,12 +50,15 @@
#include "translate-all.h"
#include "exec/memory-internal.h"
+#include "exec/ram_addr.h"
#include "qemu/cache-utils.h"
+#include "qemu/range.h"
+
//#define DEBUG_SUBPAGE
#if !defined(CONFIG_USER_ONLY)
-static int in_migration;
+static bool in_migration;
RAMList ram_list = { .blocks = QTAILQ_HEAD_INITIALIZER(ram_list.blocks) };
@@ -84,20 +87,39 @@ int use_icount;
typedef struct PhysPageEntry PhysPageEntry;
struct PhysPageEntry {
- uint16_t is_leaf : 1;
- /* index into phys_sections (is_leaf) or phys_map_nodes (!is_leaf) */
- uint16_t ptr : 15;
+ /* How many bits skip to next level (in units of L2_SIZE). 0 for a leaf. */
+ uint32_t skip : 6;
+ /* index into phys_sections (!skip) or phys_map_nodes (skip) */
+ uint32_t ptr : 26;
};
-typedef PhysPageEntry Node[L2_SIZE];
+#define PHYS_MAP_NODE_NIL (((uint32_t)~0) >> 6)
+
+/* Size of the L2 (and L3, etc) page tables. */
+#define ADDR_SPACE_BITS 64
+
+#define P_L2_BITS 9
+#define P_L2_SIZE (1 << P_L2_BITS)
+
+#define P_L2_LEVELS (((ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / P_L2_BITS) + 1)
+
+typedef PhysPageEntry Node[P_L2_SIZE];
+
+typedef struct PhysPageMap {
+ unsigned sections_nb;
+ unsigned sections_nb_alloc;
+ unsigned nodes_nb;
+ unsigned nodes_nb_alloc;
+ Node *nodes;
+ MemoryRegionSection *sections;
+} PhysPageMap;
struct AddressSpaceDispatch {
/* This is a multi-level map on the physical address space.
* The bottom level has pointers to MemoryRegionSections.
*/
PhysPageEntry phys_map;
- Node *nodes;
- MemoryRegionSection *sections;
+ PhysPageMap map;
AddressSpace *as;
};
@@ -114,20 +136,6 @@ typedef struct subpage_t {
#define PHYS_SECTION_ROM 2
#define PHYS_SECTION_WATCH 3
-typedef struct PhysPageMap {
- unsigned sections_nb;
- unsigned sections_nb_alloc;
- unsigned nodes_nb;
- unsigned nodes_nb_alloc;
- Node *nodes;
- MemoryRegionSection *sections;
-} PhysPageMap;
-
-static PhysPageMap *prev_map;
-static PhysPageMap next_map;
-
-#define PHYS_MAP_NODE_NIL (((uint16_t)~0) >> 1)
-
static void io_mem_init(void);
static void memory_map_init(void);
@@ -136,63 +144,60 @@ static MemoryRegion io_mem_watch;
#if !defined(CONFIG_USER_ONLY)
-static void phys_map_node_reserve(unsigned nodes)
+static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes)
{
- if (next_map.nodes_nb + nodes > next_map.nodes_nb_alloc) {
- next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc * 2,
- 16);
- next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc,
- next_map.nodes_nb + nodes);
- next_map.nodes = g_renew(Node, next_map.nodes,
- next_map.nodes_nb_alloc);
+ if (map->nodes_nb + nodes > map->nodes_nb_alloc) {
+ map->nodes_nb_alloc = MAX(map->nodes_nb_alloc * 2, 16);
+ map->nodes_nb_alloc = MAX(map->nodes_nb_alloc, map->nodes_nb + nodes);
+ map->nodes = g_renew(Node, map->nodes, map->nodes_nb_alloc);
}
}
-static uint16_t phys_map_node_alloc(void)
+static uint32_t phys_map_node_alloc(PhysPageMap *map)
{
unsigned i;
- uint16_t ret;
+ uint32_t ret;
- ret = next_map.nodes_nb++;
+ ret = map->nodes_nb++;
assert(ret != PHYS_MAP_NODE_NIL);
- assert(ret != next_map.nodes_nb_alloc);
- for (i = 0; i < L2_SIZE; ++i) {
- next_map.nodes[ret][i].is_leaf = 0;
- next_map.nodes[ret][i].ptr = PHYS_MAP_NODE_NIL;
+ assert(ret != map->nodes_nb_alloc);
+ for (i = 0; i < P_L2_SIZE; ++i) {
+ map->nodes[ret][i].skip = 1;
+ map->nodes[ret][i].ptr = PHYS_MAP_NODE_NIL;
}
return ret;
}
-static void phys_page_set_level(PhysPageEntry *lp, hwaddr *index,
- hwaddr *nb, uint16_t leaf,
+static void phys_page_set_level(PhysPageMap *map, PhysPageEntry *lp,
+ hwaddr *index, hwaddr *nb, uint16_t leaf,
int level)
{
PhysPageEntry *p;
int i;
- hwaddr step = (hwaddr)1 << (level * L2_BITS);
+ hwaddr step = (hwaddr)1 << (level * P_L2_BITS);
- if (!lp->is_leaf && lp->ptr == PHYS_MAP_NODE_NIL) {
- lp->ptr = phys_map_node_alloc();
- p = next_map.nodes[lp->ptr];
+ 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 < L2_SIZE; i++) {
- p[i].is_leaf = 1;
+ for (i = 0; i < P_L2_SIZE; i++) {
+ p[i].skip = 0;
p[i].ptr = PHYS_SECTION_UNASSIGNED;
}
}
} else {
- p = next_map.nodes[lp->ptr];
+ p = map->nodes[lp->ptr];
}
- lp = &p[(*index >> (level * L2_BITS)) & (L2_SIZE - 1)];
+ lp = &p[(*index >> (level * P_L2_BITS)) & (P_L2_SIZE - 1)];
- while (*nb && lp < &p[L2_SIZE]) {
+ while (*nb && lp < &p[P_L2_SIZE]) {
if ((*index & (step - 1)) == 0 && *nb >= step) {
- lp->is_leaf = true;
+ lp->skip = 0;
lp->ptr = leaf;
*index += step;
*nb -= step;
} else {
- phys_page_set_level(lp, index, nb, leaf, level - 1);
+ phys_page_set_level(map, lp, index, nb, leaf, level - 1);
}
++lp;
}
@@ -203,25 +208,95 @@ static void phys_page_set(AddressSpaceDispatch *d,
uint16_t leaf)
{
/* Wildly overreserve - it doesn't matter much. */
- phys_map_node_reserve(3 * P_L2_LEVELS);
+ phys_map_node_reserve(&d->map, 3 * P_L2_LEVELS);
- phys_page_set_level(&d->phys_map, &index, &nb, leaf, P_L2_LEVELS - 1);
+ phys_page_set_level(&d->map, &d->phys_map, &index, &nb, leaf, P_L2_LEVELS - 1);
}
-static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr index,
+/* Compact a non leaf page entry. Simply detect that the entry has a single child,
+ * and update our entry so we can skip it and go directly to the destination.
+ */
+static void phys_page_compact(PhysPageEntry *lp, Node *nodes, unsigned long *compacted)
+{
+ unsigned valid_ptr = P_L2_SIZE;
+ int valid = 0;
+ PhysPageEntry *p;
+ int i;
+
+ if (lp->ptr == PHYS_MAP_NODE_NIL) {
+ return;
+ }
+
+ p = nodes[lp->ptr];
+ for (i = 0; i < P_L2_SIZE; i++) {
+ if (p[i].ptr == PHYS_MAP_NODE_NIL) {
+ continue;
+ }
+
+ valid_ptr = i;
+ valid++;
+ if (p[i].skip) {
+ phys_page_compact(&p[i], nodes, compacted);
+ }
+ }
+
+ /* We can only compress if there's only one child. */
+ if (valid != 1) {
+ return;
+ }
+
+ assert(valid_ptr < P_L2_SIZE);
+
+ /* Don't compress if it won't fit in the # of bits we have. */
+ if (lp->skip + p[valid_ptr].skip >= (1 << 3)) {
+ return;
+ }
+
+ lp->ptr = p[valid_ptr].ptr;
+ if (!p[valid_ptr].skip) {
+ /* If our only child is a leaf, make this a leaf. */
+ /* By design, we should have made this node a leaf to begin with so we
+ * should never reach here.
+ * But since it's so simple to handle this, let's do it just in case we
+ * change this rule.
+ */
+ lp->skip = 0;
+ } else {
+ lp->skip += p[valid_ptr].skip;
+ }
+}
+
+static void phys_page_compact_all(AddressSpaceDispatch *d, int nodes_nb)
+{
+ DECLARE_BITMAP(compacted, nodes_nb);
+
+ if (d->phys_map.skip) {
+ phys_page_compact(&d->phys_map, d->map.nodes, compacted);
+ }
+}
+
+static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr,
Node *nodes, MemoryRegionSection *sections)
{
PhysPageEntry *p;
+ hwaddr index = addr >> TARGET_PAGE_BITS;
int i;
- for (i = P_L2_LEVELS - 1; i >= 0 && !lp.is_leaf; i--) {
+ for (i = P_L2_LEVELS; lp.skip && (i -= lp.skip) >= 0;) {
if (lp.ptr == PHYS_MAP_NODE_NIL) {
return &sections[PHYS_SECTION_UNASSIGNED];
}
p = nodes[lp.ptr];
- lp = p[(index >> (i * L2_BITS)) & (L2_SIZE - 1)];
+ lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)];
+ }
+
+ if (sections[lp.ptr].size.hi ||
+ range_covers_byte(sections[lp.ptr].offset_within_address_space,
+ sections[lp.ptr].size.lo, addr)) {
+ return &sections[lp.ptr];
+ } else {
+ return &sections[PHYS_SECTION_UNASSIGNED];
}
- return &sections[lp.ptr];
}
bool memory_region_is_unassigned(MemoryRegion *mr)
@@ -237,11 +312,10 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d,
MemoryRegionSection *section;
subpage_t *subpage;
- section = phys_page_find(d->phys_map, addr >> TARGET_PAGE_BITS,
- d->nodes, d->sections);
+ section = phys_page_find(d->phys_map, addr, d->map.nodes, d->map.sections);
if (resolve_subpage && section->mr->subpage) {
subpage = container_of(section->mr, subpage_t, iomem);
- section = &d->sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
+ section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
}
return section;
}
@@ -651,11 +725,14 @@ found:
return block;
}
-static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
- uintptr_t length)
+static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length)
{
- RAMBlock *block;
ram_addr_t start1;
+ RAMBlock *block;
+ ram_addr_t end;
+
+ end = TARGET_PAGE_ALIGN(start + length);
+ start &= TARGET_PAGE_MASK;
block = qemu_get_ram_block(start);
assert(block == qemu_get_ram_block(end - 1));
@@ -664,29 +741,21 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t end,
}
/* Note: start and end must be within the same ram block. */
-void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
- int dirty_flags)
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
+ unsigned client)
{
- uintptr_t length;
-
- start &= TARGET_PAGE_MASK;
- end = TARGET_PAGE_ALIGN(end);
-
- length = end - start;
if (length == 0)
return;
- cpu_physical_memory_mask_dirty_range(start, length, dirty_flags);
+ cpu_physical_memory_clear_dirty_range(start, length, client);
if (tcg_enabled()) {
- tlb_reset_dirty_range_all(start, end, length);
+ tlb_reset_dirty_range_all(start, length);
}
}
-static int cpu_physical_memory_set_dirty_tracking(int enable)
+static void cpu_physical_memory_set_dirty_tracking(bool enable)
{
- int ret = 0;
in_migration = enable;
- return ret;
}
hwaddr memory_region_section_get_iotlb(CPUArchState *env,
@@ -709,7 +778,7 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env,
iotlb |= PHYS_SECTION_ROM;
}
} else {
- iotlb = section - address_space_memory.dispatch->sections;
+ iotlb = section - address_space_memory.dispatch->map.sections;
iotlb += xlat;
}
@@ -748,23 +817,23 @@ void phys_mem_set_alloc(void *(*alloc)(size_t))
phys_mem_alloc = alloc;
}
-static uint16_t phys_section_add(MemoryRegionSection *section)
+static uint16_t phys_section_add(PhysPageMap *map,
+ MemoryRegionSection *section)
{
/* The physical section number is ORed with a page-aligned
* pointer to produce the iotlb entries. Thus it should
* never overflow into the page-aligned value.
*/
- assert(next_map.sections_nb < TARGET_PAGE_SIZE);
+ assert(map->sections_nb < TARGET_PAGE_SIZE);
- if (next_map.sections_nb == next_map.sections_nb_alloc) {
- next_map.sections_nb_alloc = MAX(next_map.sections_nb_alloc * 2,
- 16);
- next_map.sections = g_renew(MemoryRegionSection, next_map.sections,
- next_map.sections_nb_alloc);
+ if (map->sections_nb == map->sections_nb_alloc) {
+ map->sections_nb_alloc = MAX(map->sections_nb_alloc * 2, 16);
+ map->sections = g_renew(MemoryRegionSection, map->sections,
+ map->sections_nb_alloc);
}
- next_map.sections[next_map.sections_nb] = *section;
+ map->sections[map->sections_nb] = *section;
memory_region_ref(section->mr);
- return next_map.sections_nb++;
+ return map->sections_nb++;
}
static void phys_section_destroy(MemoryRegion *mr)
@@ -786,7 +855,6 @@ static void phys_sections_free(PhysPageMap *map)
}
g_free(map->sections);
g_free(map->nodes);
- g_free(map);
}
static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *section)
@@ -794,8 +862,8 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti
subpage_t *subpage;
hwaddr base = section->offset_within_address_space
& TARGET_PAGE_MASK;
- MemoryRegionSection *existing = phys_page_find(d->phys_map, base >> TARGET_PAGE_BITS,
- next_map.nodes, next_map.sections);
+ MemoryRegionSection *existing = phys_page_find(d->phys_map, base,
+ d->map.nodes, d->map.sections);
MemoryRegionSection subsection = {
.offset_within_address_space = base,
.size = int128_make64(TARGET_PAGE_SIZE),
@@ -808,13 +876,14 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti
subpage = subpage_init(d->as, base);
subsection.mr = &subpage->iomem;
phys_page_set(d, base >> TARGET_PAGE_BITS, 1,
- phys_section_add(&subsection));
+ phys_section_add(&d->map, &subsection));
} else {
subpage = container_of(existing->mr, subpage_t, iomem);
}
start = section->offset_within_address_space & ~TARGET_PAGE_MASK;
end = start + int128_get64(section->size) - 1;
- subpage_register(subpage, start, end, phys_section_add(section));
+ subpage_register(subpage, start, end,
+ phys_section_add(&d->map, section));
}
@@ -822,7 +891,7 @@ static void register_multipage(AddressSpaceDispatch *d,
MemoryRegionSection *section)
{
hwaddr start_addr = section->offset_within_address_space;
- uint16_t section_index = phys_section_add(section);
+ uint16_t section_index = phys_section_add(&d->map, section);
uint64_t num_pages = int128_get64(int128_rshift(section->size,
TARGET_PAGE_BITS));
@@ -1138,6 +1207,9 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
MemoryRegion *mr)
{
RAMBlock *block, *new_block;
+ ram_addr_t old_ram_size, new_ram_size;
+
+ old_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
size = TARGET_PAGE_ALIGN(size);
new_block = g_malloc0(sizeof(*new_block));
@@ -1198,11 +1270,17 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
ram_list.version++;
qemu_mutex_unlock_ramlist();
- ram_list.phys_dirty = g_realloc(ram_list.phys_dirty,
- last_ram_offset() >> TARGET_PAGE_BITS);
- memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
- 0, size >> TARGET_PAGE_BITS);
- cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff);
+ new_ram_size = last_ram_offset() >> TARGET_PAGE_BITS;
+
+ if (new_ram_size > old_ram_size) {
+ int i;
+ for (i = 0; i < DIRTY_MEMORY_NUM; i++) {
+ ram_list.dirty_memory[i] =
+ bitmap_zero_extend(ram_list.dirty_memory[i],
+ old_ram_size, new_ram_size);
+ }
+ }
+ cpu_physical_memory_set_dirty_range(new_block->offset, size);
qemu_ram_setup_dump(new_block->host, size);
qemu_madvise(new_block->host, size, QEMU_MADV_HUGEPAGE);
@@ -1412,11 +1490,8 @@ found:
static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
uint64_t val, unsigned size)
{
- int dirty_flags;
- dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
- if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+ if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
tb_invalidate_phys_page_fast(ram_addr, size);
- dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
}
switch (size) {
case 1:
@@ -1431,11 +1506,11 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
default:
abort();
}
- dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
- cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags);
+ cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_MIGRATION);
+ cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_VGA);
/* we remove the notdirty callback only if the code has been
flushed */
- if (dirty_flags == 0xff) {
+ if (!cpu_physical_memory_is_clean(ram_addr)) {
CPUArchState *env = current_cpu->env_ptr;
tlb_set_dirty(env, env->mem_io_vaddr);
}
@@ -1641,7 +1716,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
return mmio;
}
-static uint16_t dummy_section(MemoryRegion *mr)
+static uint16_t dummy_section(PhysPageMap *map, MemoryRegion *mr)
{
MemoryRegionSection section = {
.mr = mr,
@@ -1650,12 +1725,13 @@ static uint16_t dummy_section(MemoryRegion *mr)
.size = int128_2_64(),
};
- return phys_section_add(&section);
+ return phys_section_add(map, &section);
}
MemoryRegion *iotlb_to_region(hwaddr index)
{
- return address_space_memory.dispatch->sections[index & ~TARGET_PAGE_MASK].mr;
+ return address_space_memory.dispatch->map.sections[
+ index & ~TARGET_PAGE_MASK].mr;
}
static void io_mem_init(void)
@@ -1672,9 +1748,19 @@ static void io_mem_init(void)
static void mem_begin(MemoryListener *listener)
{
AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
- AddressSpaceDispatch *d = g_new(AddressSpaceDispatch, 1);
+ AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1);
+ uint16_t n;
+
+ n = dummy_section(&d->map, &io_mem_unassigned);
+ assert(n == PHYS_SECTION_UNASSIGNED);
+ n = dummy_section(&d->map, &io_mem_notdirty);
+ assert(n == PHYS_SECTION_NOTDIRTY);
+ n = dummy_section(&d->map, &io_mem_rom);
+ assert(n == PHYS_SECTION_ROM);
+ n = dummy_section(&d->map, &io_mem_watch);
+ assert(n == PHYS_SECTION_WATCH);
- d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .is_leaf = 0 };
+ d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip = 1 };
d->as = as;
as->next_dispatch = d;
}
@@ -1685,37 +1771,14 @@ static void mem_commit(MemoryListener *listener)
AddressSpaceDispatch *cur = as->dispatch;
AddressSpaceDispatch *next = as->next_dispatch;
- next->nodes = next_map.nodes;
- next->sections = next_map.sections;
+ phys_page_compact_all(next, next->map.nodes_nb);
as->dispatch = next;
- g_free(cur);
-}
-
-static void core_begin(MemoryListener *listener)
-{
- uint16_t n;
- prev_map = g_new(PhysPageMap, 1);
- *prev_map = next_map;
-
- memset(&next_map, 0, sizeof(next_map));
- n = dummy_section(&io_mem_unassigned);
- assert(n == PHYS_SECTION_UNASSIGNED);
- n = dummy_section(&io_mem_notdirty);
- assert(n == PHYS_SECTION_NOTDIRTY);
- n = dummy_section(&io_mem_rom);
- assert(n == PHYS_SECTION_ROM);
- n = dummy_section(&io_mem_watch);
- assert(n == PHYS_SECTION_WATCH);
-}
-
-/* This listener's commit run after the other AddressSpaceDispatch listeners'.
- * All AddressSpaceDispatch instances have switched to the next map.
- */
-static void core_commit(MemoryListener *listener)
-{
- phys_sections_free(prev_map);
+ if (cur) {
+ phys_sections_free(&cur->map);
+ g_free(cur);
+ }
}
static void tcg_commit(MemoryListener *listener)
@@ -1734,17 +1797,15 @@ static void tcg_commit(MemoryListener *listener)
static void core_log_global_start(MemoryListener *listener)
{
- cpu_physical_memory_set_dirty_tracking(1);
+ cpu_physical_memory_set_dirty_tracking(true);
}
static void core_log_global_stop(MemoryListener *listener)
{
- cpu_physical_memory_set_dirty_tracking(0);
+ cpu_physical_memory_set_dirty_tracking(false);
}
static MemoryListener core_memory_listener = {
- .begin = core_begin,
- .commit = core_commit,
.log_global_start = core_log_global_start,
.log_global_stop = core_log_global_stop,
.priority = 1,
@@ -1779,7 +1840,8 @@ void address_space_destroy_dispatch(AddressSpace *as)
static void memory_map_init(void)
{
system_memory = g_malloc(sizeof(*system_memory));
- memory_region_init(system_memory, NULL, "system", INT64_MAX);
+
+ memory_region_init(system_memory, NULL, "system", UINT64_MAX);
address_space_init(&address_space_memory, system_memory, "memory");
system_io = g_malloc(sizeof(*system_io));
@@ -1851,11 +1913,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
static void invalidate_and_set_dirty(hwaddr addr,
hwaddr length)
{
- if (!cpu_physical_memory_is_dirty(addr)) {
+ if (cpu_physical_memory_is_clean(addr)) {
/* invalidate code */
tb_invalidate_phys_page_range(addr, addr + length, 0);
/* set dirty bit */
- cpu_physical_memory_set_dirty_flags(addr, (0xff & ~CODE_DIRTY_FLAG));
+ cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_VGA);
+ cpu_physical_memory_set_dirty_flag(addr, DIRTY_MEMORY_MIGRATION);
}
xen_modified_memory(addr, length);
}
@@ -2466,12 +2529,13 @@ void stl_phys_notdirty(hwaddr addr, uint32_t val)
stl_p(ptr, val);
if (unlikely(in_migration)) {
- if (!cpu_physical_memory_is_dirty(addr1)) {
+ 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_flags(
- addr1, (0xff & ~CODE_DIRTY_FLAG));
+ cpu_physical_memory_set_dirty_flag(addr1,
+ DIRTY_MEMORY_MIGRATION);
+ cpu_physical_memory_set_dirty_flag(addr1, DIRTY_MEMORY_VGA);
}
}
}
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index dbda61bc8e..e0ea599769 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -42,6 +42,9 @@ these four paragraphs for those parts of this code that are retained.
#include "fpu/softfloat.h"
+/* We only need stdlib for abort() */
+#include <stdlib.h>
+
/*----------------------------------------------------------------------------
| Primitive arithmetic functions, including multi-word arithmetic, and
| division and square root approximations. (Can be specialized to target if
@@ -59,21 +62,6 @@ these four paragraphs for those parts of this code that are retained.
*----------------------------------------------------------------------------*/
#include "softfloat-specialize.h"
-void set_float_rounding_mode(int val STATUS_PARAM)
-{
- STATUS(float_rounding_mode) = val;
-}
-
-void set_float_exception_flags(int val STATUS_PARAM)
-{
- STATUS(float_exception_flags) = val;
-}
-
-void set_floatx80_rounding_precision(int val STATUS_PARAM)
-{
- STATUS(floatx80_rounding_precision) = val;
-}
-
/*----------------------------------------------------------------------------
| Returns the fraction bits of the half-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
@@ -121,20 +109,22 @@ static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM)
roundingMode = STATUS(float_rounding_mode);
roundNearestEven = ( roundingMode == float_round_nearest_even );
- roundIncrement = 0x40;
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- roundIncrement = 0;
- }
- else {
- roundIncrement = 0x7F;
- if ( zSign ) {
- if ( roundingMode == float_round_up ) roundIncrement = 0;
- }
- else {
- if ( roundingMode == float_round_down ) roundIncrement = 0;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ roundIncrement = 0x40;
+ break;
+ case float_round_to_zero:
+ roundIncrement = 0;
+ break;
+ case float_round_up:
+ roundIncrement = zSign ? 0 : 0x7f;
+ break;
+ case float_round_down:
+ roundIncrement = zSign ? 0x7f : 0;
+ break;
+ default:
+ abort();
}
roundBits = absZ & 0x7F;
absZ = ( absZ + roundIncrement )>>7;
@@ -170,19 +160,22 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU
roundingMode = STATUS(float_rounding_mode);
roundNearestEven = ( roundingMode == float_round_nearest_even );
- increment = ( (int64_t) absZ1 < 0 );
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- increment = 0;
- }
- else {
- if ( zSign ) {
- increment = ( roundingMode == float_round_down ) && absZ1;
- }
- else {
- increment = ( roundingMode == float_round_up ) && absZ1;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t) absZ1 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && absZ1;
+ break;
+ case float_round_down:
+ increment = zSign && absZ1;
+ break;
+ default:
+ abort();
}
if ( increment ) {
++absZ0;
@@ -204,6 +197,61 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU
}
/*----------------------------------------------------------------------------
+| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
+| `absZ1', with binary point between bits 63 and 64 (between the input words),
+| and returns the properly rounded 64-bit unsigned integer corresponding to the
+| input. Ordinarily, the fixed-point input is simply rounded to an integer,
+| with the inexact exception raised if the input cannot be represented exactly
+| as an integer. However, if the fixed-point input is too large, the invalid
+| exception is raised and the largest unsigned integer is returned.
+*----------------------------------------------------------------------------*/
+
+static int64 roundAndPackUint64(flag zSign, uint64_t absZ0,
+ uint64_t absZ1 STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = (roundingMode == float_round_nearest_even);
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t)absZ1 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && absZ1;
+ break;
+ case float_round_down:
+ increment = zSign && absZ1;
+ break;
+ default:
+ abort();
+ }
+ if (increment) {
+ ++absZ0;
+ if (absZ0 == 0) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return LIT64(0xFFFFFFFFFFFFFFFF);
+ }
+ absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven);
+ }
+
+ if (zSign && absZ0) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+
+ if (absZ1) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return absZ0;
+}
+
+/*----------------------------------------------------------------------------
| Returns the fraction bits of the single-precision floating-point value `a'.
*----------------------------------------------------------------------------*/
@@ -319,20 +367,23 @@ static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig
roundingMode = STATUS(float_rounding_mode);
roundNearestEven = ( roundingMode == float_round_nearest_even );
- roundIncrement = 0x40;
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- roundIncrement = 0;
- }
- else {
- roundIncrement = 0x7F;
- if ( zSign ) {
- if ( roundingMode == float_round_up ) roundIncrement = 0;
- }
- else {
- if ( roundingMode == float_round_down ) roundIncrement = 0;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ roundIncrement = 0x40;
+ break;
+ case float_round_to_zero:
+ roundIncrement = 0;
+ break;
+ case float_round_up:
+ roundIncrement = zSign ? 0 : 0x7f;
+ break;
+ case float_round_down:
+ roundIncrement = zSign ? 0x7f : 0;
+ break;
+ default:
+ abort();
+ break;
}
roundBits = zSig & 0x7F;
if ( 0xFD <= (uint16_t) zExp ) {
@@ -501,20 +552,22 @@ static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig
roundingMode = STATUS(float_rounding_mode);
roundNearestEven = ( roundingMode == float_round_nearest_even );
- roundIncrement = 0x200;
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- roundIncrement = 0;
- }
- else {
- roundIncrement = 0x3FF;
- if ( zSign ) {
- if ( roundingMode == float_round_up ) roundIncrement = 0;
- }
- else {
- if ( roundingMode == float_round_down ) roundIncrement = 0;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ roundIncrement = 0x200;
+ break;
+ case float_round_to_zero:
+ roundIncrement = 0;
+ break;
+ case float_round_up:
+ roundIncrement = zSign ? 0 : 0x3ff;
+ break;
+ case float_round_down:
+ roundIncrement = zSign ? 0x3ff : 0;
+ break;
+ default:
+ abort();
}
roundBits = zSig & 0x3FF;
if ( 0x7FD <= (uint16_t) zExp ) {
@@ -684,19 +737,21 @@ static floatx80
goto precision80;
}
zSig0 |= ( zSig1 != 0 );
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- roundIncrement = 0;
- }
- else {
- roundIncrement = roundMask;
- if ( zSign ) {
- if ( roundingMode == float_round_up ) roundIncrement = 0;
- }
- else {
- if ( roundingMode == float_round_down ) roundIncrement = 0;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ break;
+ case float_round_to_zero:
+ roundIncrement = 0;
+ break;
+ case float_round_up:
+ roundIncrement = zSign ? 0 : roundMask;
+ break;
+ case float_round_down:
+ roundIncrement = zSign ? roundMask : 0;
+ break;
+ default:
+ abort();
}
roundBits = zSig0 & roundMask;
if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) {
@@ -743,19 +798,22 @@ static floatx80
if ( zSig0 == 0 ) zExp = 0;
return packFloatx80( zSign, zExp, zSig0 );
precision80:
- increment = ( (int64_t) zSig1 < 0 );
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- increment = 0;
- }
- else {
- if ( zSign ) {
- increment = ( roundingMode == float_round_down ) && zSig1;
- }
- else {
- increment = ( roundingMode == float_round_up ) && zSig1;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t)zSig1 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && zSig1;
+ break;
+ case float_round_down:
+ increment = zSign && zSig1;
+ break;
+ default:
+ abort();
}
if ( 0x7FFD <= (uint32_t) ( zExp - 1 ) ) {
if ( ( 0x7FFE < zExp )
@@ -785,16 +843,22 @@ static floatx80
zExp = 0;
if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR);
if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
- if ( roundNearestEven ) {
- increment = ( (int64_t) zSig1 < 0 );
- }
- else {
- if ( zSign ) {
- increment = ( roundingMode == float_round_down ) && zSig1;
- }
- else {
- increment = ( roundingMode == float_round_up ) && zSig1;
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t)zSig1 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && zSig1;
+ break;
+ case float_round_down:
+ increment = zSign && zSig1;
+ break;
+ default:
+ abort();
}
if ( increment ) {
++zSig0;
@@ -994,19 +1058,22 @@ static float128
roundingMode = STATUS(float_rounding_mode);
roundNearestEven = ( roundingMode == float_round_nearest_even );
- increment = ( (int64_t) zSig2 < 0 );
- if ( ! roundNearestEven ) {
- if ( roundingMode == float_round_to_zero ) {
- increment = 0;
- }
- else {
- if ( zSign ) {
- increment = ( roundingMode == float_round_down ) && zSig2;
- }
- else {
- increment = ( roundingMode == float_round_up ) && zSig2;
- }
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t)zSig2 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && zSig2;
+ break;
+ case float_round_down:
+ increment = zSign && zSig2;
+ break;
+ default:
+ abort();
}
if ( 0x7FFD <= (uint32_t) zExp ) {
if ( ( 0x7FFD < zExp )
@@ -1054,16 +1121,22 @@ static float128
zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
zExp = 0;
if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR);
- if ( roundNearestEven ) {
- increment = ( (int64_t) zSig2 < 0 );
- }
- else {
- if ( zSign ) {
- increment = ( roundingMode == float_round_down ) && zSig2;
- }
- else {
- increment = ( roundingMode == float_round_up ) && zSig2;
- }
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ case float_round_ties_away:
+ increment = ((int64_t)zSig2 < 0);
+ break;
+ case float_round_to_zero:
+ increment = 0;
+ break;
+ case float_round_up:
+ increment = !zSign && zSig2;
+ break;
+ case float_round_down:
+ increment = zSign && zSig2;
+ break;
+ default:
+ abort();
}
}
}
@@ -1121,7 +1194,7 @@ static float128
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float32 int32_to_float32( int32 a STATUS_PARAM )
+float32 int32_to_float32(int32_t a STATUS_PARAM)
{
flag zSign;
@@ -1138,7 +1211,7 @@ float32 int32_to_float32( int32 a STATUS_PARAM )
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float64 int32_to_float64( int32 a STATUS_PARAM )
+float64 int32_to_float64(int32_t a STATUS_PARAM)
{
flag zSign;
uint32 absA;
@@ -1161,7 +1234,7 @@ float64 int32_to_float64( int32 a STATUS_PARAM )
| Arithmetic.
*----------------------------------------------------------------------------*/
-floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
+floatx80 int32_to_floatx80(int32_t a STATUS_PARAM)
{
flag zSign;
uint32 absA;
@@ -1183,7 +1256,7 @@ floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float128 int32_to_float128( int32 a STATUS_PARAM )
+float128 int32_to_float128(int32_t a STATUS_PARAM)
{
flag zSign;
uint32 absA;
@@ -1205,7 +1278,7 @@ float128 int32_to_float128( int32 a STATUS_PARAM )
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float32 int64_to_float32( int64 a STATUS_PARAM )
+float32 int64_to_float32(int64_t a STATUS_PARAM)
{
flag zSign;
uint64 absA;
@@ -1231,7 +1304,7 @@ float32 int64_to_float32( int64 a STATUS_PARAM )
}
-float32 uint64_to_float32( uint64 a STATUS_PARAM )
+float32 uint64_to_float32(uint64_t a STATUS_PARAM)
{
int8 shiftCount;
@@ -1258,7 +1331,7 @@ float32 uint64_to_float32( uint64 a STATUS_PARAM )
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float64 int64_to_float64( int64 a STATUS_PARAM )
+float64 int64_to_float64(int64_t a STATUS_PARAM)
{
flag zSign;
@@ -1271,7 +1344,7 @@ float64 int64_to_float64( int64 a STATUS_PARAM )
}
-float64 uint64_to_float64(uint64 a STATUS_PARAM)
+float64 uint64_to_float64(uint64_t a STATUS_PARAM)
{
int exp = 0x43C;
@@ -1292,7 +1365,7 @@ float64 uint64_to_float64(uint64 a STATUS_PARAM)
| Arithmetic.
*----------------------------------------------------------------------------*/
-floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
+floatx80 int64_to_floatx80(int64_t a STATUS_PARAM)
{
flag zSign;
uint64 absA;
@@ -1312,7 +1385,7 @@ floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/
-float128 int64_to_float128( int64 a STATUS_PARAM )
+float128 int64_to_float128(int64_t a STATUS_PARAM)
{
flag zSign;
uint64 absA;
@@ -1339,7 +1412,7 @@ float128 int64_to_float128( int64 a STATUS_PARAM )
}
-float128 uint64_to_float128(uint64 a STATUS_PARAM)
+float128 uint64_to_float128(uint64_t a STATUS_PARAM)
{
if (a == 0) {
return float128_zero;
@@ -1509,6 +1582,52 @@ int64 float32_to_int64( float32 a STATUS_PARAM )
/*----------------------------------------------------------------------------
| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit unsigned integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| unsigned integer is returned. Otherwise, if the conversion overflows, the
+| largest unsigned integer is returned. If the 'a' is negative, the result
+| is rounded and zero is returned; values that do not round to zero will
+| raise the inexact exception flag.
+*----------------------------------------------------------------------------*/
+
+uint64 float32_to_uint64(float32 a STATUS_PARAM)
+{
+ flag aSign;
+ int_fast16_t aExp, shiftCount;
+ uint32_t aSig;
+ uint64_t aSig64, aSigExtra;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac(a);
+ aExp = extractFloat32Exp(a);
+ aSign = extractFloat32Sign(a);
+ if ((aSign) && (aExp > 126)) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ if (float32_is_any_nan(a)) {
+ return LIT64(0xFFFFFFFFFFFFFFFF);
+ } else {
+ return 0;
+ }
+ }
+ shiftCount = 0xBE - aExp;
+ if (aExp) {
+ aSig |= 0x00800000;
+ }
+ if (shiftCount < 0) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return LIT64(0xFFFFFFFFFFFFFFFF);
+ }
+
+ aSig64 = aSig;
+ aSig64 <<= 40;
+ shift64ExtraRightJamming(aSig64, 0, shiftCount, &aSig64, &aSigExtra);
+ return roundAndPackUint64(aSign, aSig64, aSigExtra STATUS_VAR);
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
| `a' to the 64-bit two's complement integer format. The conversion is
| performed according to the IEC/IEEE Standard for Binary Floating-Point
| Arithmetic, except that the conversion is always rounded toward zero. If
@@ -1656,7 +1775,6 @@ float32 float32_round_to_int( float32 a STATUS_PARAM)
flag aSign;
int_fast16_t aExp;
uint32_t lastBitMask, roundBitsMask;
- int8 roundingMode;
uint32_t z;
a = float32_squash_input_denormal(a STATUS_VAR);
@@ -1677,6 +1795,11 @@ float32 float32_round_to_int( float32 a STATUS_PARAM)
return packFloat32( aSign, 0x7F, 0 );
}
break;
+ case float_round_ties_away:
+ if (aExp == 0x7E) {
+ return packFloat32(aSign, 0x7F, 0);
+ }
+ break;
case float_round_down:
return make_float32(aSign ? 0xBF800000 : 0);
case float_round_up:
@@ -1688,15 +1811,30 @@ float32 float32_round_to_int( float32 a STATUS_PARAM)
lastBitMask <<= 0x96 - aExp;
roundBitsMask = lastBitMask - 1;
z = float32_val(a);
- roundingMode = STATUS(float_rounding_mode);
- if ( roundingMode == float_round_nearest_even ) {
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
z += lastBitMask>>1;
- if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
- }
- else if ( roundingMode != float_round_to_zero ) {
- if ( extractFloat32Sign( make_float32(z) ) ^ ( roundingMode == float_round_up ) ) {
+ if ((z & roundBitsMask) == 0) {
+ z &= ~lastBitMask;
+ }
+ break;
+ case float_round_ties_away:
+ z += lastBitMask >> 1;
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ if (!extractFloat32Sign(make_float32(z))) {
+ z += roundBitsMask;
+ }
+ break;
+ case float_round_down:
+ if (extractFloat32Sign(make_float32(z))) {
z += roundBitsMask;
}
+ break;
+ default:
+ abort();
}
z &= ~ roundBitsMask;
if ( z != float32_val(a) ) STATUS(float_exception_flags) |= float_flag_inexact;
@@ -3005,6 +3143,128 @@ static float16 packFloat16(flag zSign, int_fast16_t zExp, uint16_t zSig)
(((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig);
}
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper half-precision floating-
+| point value corresponding to the abstract input. Ordinarily, the abstract
+| value is simply rounded and packed into the half-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal half-
+| precision floating-point number.
+| The `ieee' flag indicates whether to use IEEE standard half precision, or
+| ARM-style "alternative representation", which omits the NaN and Inf
+| encodings in order to raise the maximum representable exponent by one.
+| The input significand `zSig' has its binary point between bits 22
+| and 23, which is 13 bits to the left of the usual location. This shifted
+| significand must be normalized or smaller. If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding. In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| Note the slightly odd position of the binary point in zSig compared with the
+| other roundAndPackFloat functions. This should probably be fixed if we
+| need to implement more float16 routines than just conversion.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp,
+ uint32_t zSig, flag ieee STATUS_PARAM)
+{
+ int maxexp = ieee ? 29 : 30;
+ uint32_t mask;
+ uint32_t increment;
+ bool rounding_bumps_exp;
+ bool is_tiny = false;
+
+ /* Calculate the mask of bits of the mantissa which are not
+ * representable in half-precision and will be lost.
+ */
+ if (zExp < 1) {
+ /* Will be denormal in halfprec */
+ mask = 0x00ffffff;
+ if (zExp >= -11) {
+ mask >>= 11 + zExp;
+ }
+ } else {
+ /* Normal number in halfprec */
+ mask = 0x00001fff;
+ }
+
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
+ increment = (mask + 1) >> 1;
+ if ((zSig & mask) == increment) {
+ increment = zSig & (increment << 1);
+ }
+ break;
+ case float_round_ties_away:
+ increment = (mask + 1) >> 1;
+ break;
+ case float_round_up:
+ increment = zSign ? 0 : mask;
+ break;
+ case float_round_down:
+ increment = zSign ? mask : 0;
+ break;
+ default: /* round_to_zero */
+ increment = 0;
+ break;
+ }
+
+ rounding_bumps_exp = (zSig + increment >= 0x01000000);
+
+ if (zExp > maxexp || (zExp == maxexp && rounding_bumps_exp)) {
+ if (ieee) {
+ float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat16(zSign, 0x1f, 0);
+ } else {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return packFloat16(zSign, 0x1f, 0x3ff);
+ }
+ }
+
+ if (zExp < 0) {
+ /* Note that flush-to-zero does not affect half-precision results */
+ is_tiny =
+ (STATUS(float_detect_tininess) == float_tininess_before_rounding)
+ || (zExp < -1)
+ || (!rounding_bumps_exp);
+ }
+ if (zSig & mask) {
+ float_raise(float_flag_inexact STATUS_VAR);
+ if (is_tiny) {
+ float_raise(float_flag_underflow STATUS_VAR);
+ }
+ }
+
+ zSig += increment;
+ if (rounding_bumps_exp) {
+ zSig >>= 1;
+ zExp++;
+ }
+
+ if (zExp < -10) {
+ return packFloat16(zSign, 0, 0);
+ }
+ if (zExp < 0) {
+ zSig >>= -zExp;
+ zExp = 0;
+ }
+ return packFloat16(zSign, zExp, zSig >> 13);
+}
+
+static void normalizeFloat16Subnormal(uint32_t aSig, int_fast16_t *zExpPtr,
+ uint32_t *zSigPtr)
+{
+ int8_t shiftCount = countLeadingZeros32(aSig) - 21;
+ *zSigPtr = aSig << shiftCount;
+ *zExpPtr = 1 - shiftCount;
+}
+
/* Half precision floats come in two formats: standard IEEE and "ARM" format.
The latter gains extra exponent range by omitting the NaN/Inf encodings. */
@@ -3025,15 +3285,12 @@ float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM)
return packFloat32(aSign, 0xff, 0);
}
if (aExp == 0) {
- int8 shiftCount;
-
if (aSig == 0) {
return packFloat32(aSign, 0, 0);
}
- shiftCount = countLeadingZeros32( aSig ) - 21;
- aSig = aSig << shiftCount;
- aExp = -shiftCount;
+ normalizeFloat16Subnormal(aSig, &aExp, &aSig);
+ aExp--;
}
return packFloat32( aSign, aExp + 0x70, aSig << 13);
}
@@ -3043,9 +3300,7 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
flag aSign;
int_fast16_t aExp;
uint32_t aSig;
- uint32_t mask;
- uint32_t increment;
- int8 roundingMode;
+
a = float32_squash_input_denormal(a STATUS_VAR);
aSig = extractFloat32Frac( a );
@@ -3054,11 +3309,12 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
if ( aExp == 0xFF ) {
if (aSig) {
/* Input is a NaN */
- float16 r = commonNaNToFloat16( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR );
if (!ieee) {
+ float_raise(float_flag_invalid STATUS_VAR);
return packFloat16(aSign, 0, 0);
}
- return r;
+ return commonNaNToFloat16(
+ float32ToCommonNaN(a STATUS_VAR) STATUS_VAR);
}
/* Infinity */
if (!ieee) {
@@ -3070,66 +3326,92 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
if (aExp == 0 && aSig == 0) {
return packFloat16(aSign, 0, 0);
}
- /* Decimal point between bits 22 and 23. */
+ /* Decimal point between bits 22 and 23. Note that we add the 1 bit
+ * even if the input is denormal; however this is harmless because
+ * the largest possible single-precision denormal is still smaller
+ * than the smallest representable half-precision denormal, and so we
+ * will end up ignoring aSig and returning via the "always return zero"
+ * codepath.
+ */
aSig |= 0x00800000;
- aExp -= 0x7f;
- if (aExp < -14) {
- mask = 0x00ffffff;
- if (aExp >= -24) {
- mask >>= 25 + aExp;
+ aExp -= 0x71;
+
+ return roundAndPackFloat16(aSign, aExp, aSig, ieee STATUS_VAR);
+}
+
+float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM)
+{
+ flag aSign;
+ int_fast16_t aExp;
+ uint32_t aSig;
+
+ aSign = extractFloat16Sign(a);
+ aExp = extractFloat16Exp(a);
+ aSig = extractFloat16Frac(a);
+
+ if (aExp == 0x1f && ieee) {
+ if (aSig) {
+ return commonNaNToFloat64(
+ float16ToCommonNaN(a STATUS_VAR) STATUS_VAR);
}
- } else {
- mask = 0x00001fff;
+ return packFloat64(aSign, 0x7ff, 0);
}
- if (aSig & mask) {
- float_raise( float_flag_underflow STATUS_VAR );
- roundingMode = STATUS(float_rounding_mode);
- switch (roundingMode) {
- case float_round_nearest_even:
- increment = (mask + 1) >> 1;
- if ((aSig & mask) == increment) {
- increment = aSig & (increment << 1);
- }
- break;
- case float_round_up:
- increment = aSign ? 0 : mask;
- break;
- case float_round_down:
- increment = aSign ? mask : 0;
- break;
- default: /* round_to_zero */
- increment = 0;
- break;
- }
- aSig += increment;
- if (aSig >= 0x01000000) {
- aSig >>= 1;
- aExp++;
+ if (aExp == 0) {
+ if (aSig == 0) {
+ return packFloat64(aSign, 0, 0);
}
- } else if (aExp < -14
- && STATUS(float_detect_tininess) == float_tininess_before_rounding) {
- float_raise( float_flag_underflow STATUS_VAR);
+
+ normalizeFloat16Subnormal(aSig, &aExp, &aSig);
+ aExp--;
}
+ return packFloat64(aSign, aExp + 0x3f0, ((uint64_t)aSig) << 42);
+}
- if (ieee) {
- if (aExp > 15) {
- float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
- return packFloat16(aSign, 0x1f, 0);
+float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM)
+{
+ flag aSign;
+ int_fast16_t aExp;
+ uint64_t aSig;
+ uint32_t zSig;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac(a);
+ aExp = extractFloat64Exp(a);
+ aSign = extractFloat64Sign(a);
+ if (aExp == 0x7FF) {
+ if (aSig) {
+ /* Input is a NaN */
+ if (!ieee) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return packFloat16(aSign, 0, 0);
+ }
+ return commonNaNToFloat16(
+ float64ToCommonNaN(a STATUS_VAR) STATUS_VAR);
}
- } else {
- if (aExp > 16) {
- float_raise(float_flag_invalid | float_flag_inexact STATUS_VAR);
+ /* Infinity */
+ if (!ieee) {
+ float_raise(float_flag_invalid STATUS_VAR);
return packFloat16(aSign, 0x1f, 0x3ff);
}
+ return packFloat16(aSign, 0x1f, 0);
}
- if (aExp < -24) {
+ shift64RightJamming(aSig, 29, &aSig);
+ zSig = aSig;
+ if (aExp == 0 && zSig == 0) {
return packFloat16(aSign, 0, 0);
}
- if (aExp < -14) {
- aSig >>= -14 - aExp;
- aExp = -14;
- }
- return packFloat16(aSign, aExp + 14, aSig >> 13);
+ /* Decimal point between bits 22 and 23. Note that we add the 1 bit
+ * even if the input is denormal; however this is harmless because
+ * the largest possible single-precision denormal is still smaller
+ * than the smallest representable half-precision denormal, and so we
+ * will end up ignoring aSig and returning via the "always return zero"
+ * codepath.
+ */
+ zSig |= 0x00800000;
+ aExp -= 0x3F1;
+
+ return roundAndPackFloat16(aSign, aExp, zSig, ieee STATUS_VAR);
}
/*----------------------------------------------------------------------------
@@ -3206,7 +3488,6 @@ float64 float64_round_to_int( float64 a STATUS_PARAM )
flag aSign;
int_fast16_t aExp;
uint64_t lastBitMask, roundBitsMask;
- int8 roundingMode;
uint64_t z;
a = float64_squash_input_denormal(a STATUS_VAR);
@@ -3227,6 +3508,11 @@ float64 float64_round_to_int( float64 a STATUS_PARAM )
return packFloat64( aSign, 0x3FF, 0 );
}
break;
+ case float_round_ties_away:
+ if (aExp == 0x3FE) {
+ return packFloat64(aSign, 0x3ff, 0);
+ }
+ break;
case float_round_down:
return make_float64(aSign ? LIT64( 0xBFF0000000000000 ) : 0);
case float_round_up:
@@ -3239,15 +3525,30 @@ float64 float64_round_to_int( float64 a STATUS_PARAM )
lastBitMask <<= 0x433 - aExp;
roundBitsMask = lastBitMask - 1;
z = float64_val(a);
- roundingMode = STATUS(float_rounding_mode);
- if ( roundingMode == float_round_nearest_even ) {
- z += lastBitMask>>1;
- if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
- }
- else if ( roundingMode != float_round_to_zero ) {
- if ( extractFloat64Sign( make_float64(z) ) ^ ( roundingMode == float_round_up ) ) {
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
+ z += lastBitMask >> 1;
+ if ((z & roundBitsMask) == 0) {
+ z &= ~lastBitMask;
+ }
+ break;
+ case float_round_ties_away:
+ z += lastBitMask >> 1;
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ if (!extractFloat64Sign(make_float64(z))) {
+ z += roundBitsMask;
+ }
+ break;
+ case float_round_down:
+ if (extractFloat64Sign(make_float64(z))) {
z += roundBitsMask;
}
+ break;
+ default:
+ abort();
}
z &= ~ roundBitsMask;
if ( z != float64_val(a) )
@@ -4475,7 +4776,6 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
flag aSign;
int32 aExp;
uint64_t lastBitMask, roundBitsMask;
- int8 roundingMode;
floatx80 z;
aExp = extractFloatx80Exp( a );
@@ -4500,6 +4800,11 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
}
break;
+ case float_round_ties_away:
+ if (aExp == 0x3FFE) {
+ return packFloatx80(aSign, 0x3FFF, LIT64(0x8000000000000000));
+ }
+ break;
case float_round_down:
return
aSign ?
@@ -4516,15 +4821,30 @@ floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
lastBitMask <<= 0x403E - aExp;
roundBitsMask = lastBitMask - 1;
z = a;
- roundingMode = STATUS(float_rounding_mode);
- if ( roundingMode == float_round_nearest_even ) {
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
z.low += lastBitMask>>1;
- if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
- }
- else if ( roundingMode != float_round_to_zero ) {
- if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ if ((z.low & roundBitsMask) == 0) {
+ z.low &= ~lastBitMask;
+ }
+ break;
+ case float_round_ties_away:
+ z.low += lastBitMask >> 1;
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ if (!extractFloatx80Sign(z)) {
z.low += roundBitsMask;
}
+ break;
+ case float_round_down:
+ if (extractFloatx80Sign(z)) {
+ z.low += roundBitsMask;
+ }
+ break;
+ default:
+ abort();
}
z.low &= ~ roundBitsMask;
if ( z.low == 0 ) {
@@ -5550,7 +5870,6 @@ float128 float128_round_to_int( float128 a STATUS_PARAM )
flag aSign;
int32 aExp;
uint64_t lastBitMask, roundBitsMask;
- int8 roundingMode;
float128 z;
aExp = extractFloat128Exp( a );
@@ -5567,8 +5886,8 @@ float128 float128_round_to_int( float128 a STATUS_PARAM )
lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
roundBitsMask = lastBitMask - 1;
z = a;
- roundingMode = STATUS(float_rounding_mode);
- if ( roundingMode == float_round_nearest_even ) {
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
if ( lastBitMask ) {
add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
@@ -5579,12 +5898,30 @@ float128 float128_round_to_int( float128 a STATUS_PARAM )
if ( (uint64_t) ( z.low<<1 ) == 0 ) z.high &= ~1;
}
}
- }
- else if ( roundingMode != float_round_to_zero ) {
- if ( extractFloat128Sign( z )
- ^ ( roundingMode == float_round_up ) ) {
- add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+ break;
+ case float_round_ties_away:
+ if (lastBitMask) {
+ add128(z.high, z.low, 0, lastBitMask >> 1, &z.high, &z.low);
+ } else {
+ if ((int64_t) z.low < 0) {
+ ++z.high;
+ }
+ }
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ if (!extractFloat128Sign(z)) {
+ add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low);
+ }
+ break;
+ case float_round_down:
+ if (extractFloat128Sign(z)) {
+ add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low);
}
+ break;
+ default:
+ abort();
}
z.low &= ~ roundBitsMask;
}
@@ -5602,6 +5939,11 @@ float128 float128_round_to_int( float128 a STATUS_PARAM )
return packFloat128( aSign, 0x3FFF, 0, 0 );
}
break;
+ case float_round_ties_away:
+ if (aExp == 0x3FFE) {
+ return packFloat128(aSign, 0x3FFF, 0, 0);
+ }
+ break;
case float_round_down:
return
aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
@@ -5618,19 +5960,32 @@ float128 float128_round_to_int( float128 a STATUS_PARAM )
roundBitsMask = lastBitMask - 1;
z.low = 0;
z.high = a.high;
- roundingMode = STATUS(float_rounding_mode);
- if ( roundingMode == float_round_nearest_even ) {
+ switch (STATUS(float_rounding_mode)) {
+ case float_round_nearest_even:
z.high += lastBitMask>>1;
if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
z.high &= ~ lastBitMask;
}
- }
- else if ( roundingMode != float_round_to_zero ) {
- if ( extractFloat128Sign( z )
- ^ ( roundingMode == float_round_up ) ) {
+ break;
+ case float_round_ties_away:
+ z.high += lastBitMask>>1;
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ if (!extractFloat128Sign(z)) {
z.high |= ( a.low != 0 );
z.high += roundBitsMask;
}
+ break;
+ case float_round_down:
+ if (extractFloat128Sign(z)) {
+ z.high |= (a.low != 0);
+ z.high += roundBitsMask;
+ }
+ break;
+ default:
+ abort();
}
z.high &= ~ roundBitsMask;
}
@@ -6418,12 +6773,12 @@ int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM )
}
/* misc functions */
-float32 uint32_to_float32( uint32 a STATUS_PARAM )
+float32 uint32_to_float32(uint32_t a STATUS_PARAM)
{
return int64_to_float32(a STATUS_VAR);
}
-float64 uint32_to_float64( uint32 a STATUS_PARAM )
+float64 uint32_to_float64(uint32_t a STATUS_PARAM)
{
return int64_to_float64(a STATUS_VAR);
}
@@ -6432,17 +6787,18 @@ uint32 float32_to_uint32( float32 a STATUS_PARAM )
{
int64_t v;
uint32 res;
+ int old_exc_flags = get_float_exception_flags(status);
v = float32_to_int64(a STATUS_VAR);
if (v < 0) {
res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
} else if (v > 0xffffffff) {
res = 0xffffffff;
- float_raise( float_flag_invalid STATUS_VAR);
} else {
- res = v;
+ return v;
}
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
@@ -6450,17 +6806,58 @@ uint32 float32_to_uint32_round_to_zero( float32 a STATUS_PARAM )
{
int64_t v;
uint32 res;
+ int old_exc_flags = get_float_exception_flags(status);
v = float32_to_int64_round_to_zero(a STATUS_VAR);
if (v < 0) {
res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
} else if (v > 0xffffffff) {
res = 0xffffffff;
- float_raise( float_flag_invalid STATUS_VAR);
} else {
- res = v;
+ return v;
+ }
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
+ return res;
+}
+
+int_fast16_t float32_to_int16(float32 a STATUS_PARAM)
+{
+ int32_t v;
+ int_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
+
+ v = float32_to_int32(a STATUS_VAR);
+ if (v < -0x8000) {
+ res = -0x8000;
+ } else if (v > 0x7fff) {
+ res = 0x7fff;
+ } else {
+ return v;
}
+
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
+ return res;
+}
+
+uint_fast16_t float32_to_uint16(float32 a STATUS_PARAM)
+{
+ int32_t v;
+ uint_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
+
+ v = float32_to_int32(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ } else if (v > 0xffff) {
+ res = 0xffff;
+ } else {
+ return v;
+ }
+
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
@@ -6468,53 +6865,92 @@ uint_fast16_t float32_to_uint16_round_to_zero(float32 a STATUS_PARAM)
{
int64_t v;
uint_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
v = float32_to_int64_round_to_zero(a STATUS_VAR);
if (v < 0) {
res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
} else if (v > 0xffff) {
res = 0xffff;
- float_raise( float_flag_invalid STATUS_VAR);
} else {
- res = v;
+ return v;
}
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
uint32 float64_to_uint32( float64 a STATUS_PARAM )
{
- int64_t v;
+ uint64_t v;
uint32 res;
+ int old_exc_flags = get_float_exception_flags(status);
- v = float64_to_int64(a STATUS_VAR);
- if (v < 0) {
- res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
- } else if (v > 0xffffffff) {
+ v = float64_to_uint64(a STATUS_VAR);
+ if (v > 0xffffffff) {
res = 0xffffffff;
- float_raise( float_flag_invalid STATUS_VAR);
} else {
- res = v;
+ return v;
}
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
uint32 float64_to_uint32_round_to_zero( float64 a STATUS_PARAM )
{
- int64_t v;
+ uint64_t v;
uint32 res;
+ int old_exc_flags = get_float_exception_flags(status);
- v = float64_to_int64_round_to_zero(a STATUS_VAR);
+ v = float64_to_uint64_round_to_zero(a STATUS_VAR);
+ if (v > 0xffffffff) {
+ res = 0xffffffff;
+ } else {
+ return v;
+ }
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
+ return res;
+}
+
+int_fast16_t float64_to_int16(float64 a STATUS_PARAM)
+{
+ int64_t v;
+ int_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
+
+ v = float64_to_int32(a STATUS_VAR);
+ if (v < -0x8000) {
+ res = -0x8000;
+ } else if (v > 0x7fff) {
+ res = 0x7fff;
+ } else {
+ return v;
+ }
+
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
+ return res;
+}
+
+uint_fast16_t float64_to_uint16(float64 a STATUS_PARAM)
+{
+ int64_t v;
+ uint_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
+
+ v = float64_to_int32(a STATUS_VAR);
if (v < 0) {
res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
- } else if (v > 0xffffffff) {
- res = 0xffffffff;
- float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffff) {
+ res = 0xffff;
} else {
- res = v;
+ return v;
}
+
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
@@ -6522,41 +6958,75 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM)
{
int64_t v;
uint_fast16_t res;
+ int old_exc_flags = get_float_exception_flags(status);
v = float64_to_int64_round_to_zero(a STATUS_VAR);
if (v < 0) {
res = 0;
- float_raise( float_flag_invalid STATUS_VAR);
} else if (v > 0xffff) {
res = 0xffff;
- float_raise( float_flag_invalid STATUS_VAR);
} else {
- res = v;
+ return v;
}
+ set_float_exception_flags(old_exc_flags, status);
+ float_raise(float_flag_invalid STATUS_VAR);
return res;
}
-/* FIXME: This looks broken. */
-uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
-{
- int64_t v;
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit unsigned integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. If the conversion overflows, the
+| largest unsigned integer is returned. If 'a' is negative, the value is
+| rounded and zero is returned; negative values that do not round to zero
+| will raise the inexact exception.
+*----------------------------------------------------------------------------*/
- v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
- v += float64_val(a);
- v = float64_to_int64(make_float64(v) STATUS_VAR);
+uint64_t float64_to_uint64(float64 a STATUS_PARAM)
+{
+ flag aSign;
+ int_fast16_t aExp, shiftCount;
+ uint64_t aSig, aSigExtra;
+ a = float64_squash_input_denormal(a STATUS_VAR);
- return v - INT64_MIN;
+ aSig = extractFloat64Frac(a);
+ aExp = extractFloat64Exp(a);
+ aSign = extractFloat64Sign(a);
+ if (aSign && (aExp > 1022)) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ if (float64_is_any_nan(a)) {
+ return LIT64(0xFFFFFFFFFFFFFFFF);
+ } else {
+ return 0;
+ }
+ }
+ if (aExp) {
+ aSig |= LIT64(0x0010000000000000);
+ }
+ shiftCount = 0x433 - aExp;
+ if (shiftCount <= 0) {
+ if (0x43E < aExp) {
+ float_raise(float_flag_invalid STATUS_VAR);
+ return LIT64(0xFFFFFFFFFFFFFFFF);
+ }
+ aSigExtra = 0;
+ aSig <<= -shiftCount;
+ } else {
+ shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra);
+ }
+ return roundAndPackUint64(aSign, aSig, aSigExtra STATUS_VAR);
}
uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
{
- int64_t v;
-
- v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
- v += float64_val(a);
- v = float64_to_int64_round_to_zero(make_float64(v) STATUS_VAR);
-
- return v - INT64_MIN;
+ signed char current_rounding_mode = STATUS(float_rounding_mode);
+ set_float_rounding_mode(float_round_to_zero STATUS_VAR);
+ int64_t v = float64_to_uint64(a STATUS_VAR);
+ set_float_rounding_mode(current_rounding_mode STATUS_VAR);
+ return v;
}
#define COMPARE(s, nan_exp) \
@@ -6795,10 +7265,13 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
}
return a;
}
- if ( aExp != 0 )
+ if (aExp != 0) {
aSig |= 0x00800000;
- else if ( aSig == 0 )
+ } else if (aSig == 0) {
return a;
+ } else {
+ aExp++;
+ }
if (n > 0x200) {
n = 0x200;
@@ -6828,10 +7301,13 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
}
return a;
}
- if ( aExp != 0 )
+ if (aExp != 0) {
aSig |= LIT64( 0x0010000000000000 );
- else if ( aSig == 0 )
+ } else if (aSig == 0) {
return a;
+ } else {
+ aExp++;
+ }
if (n > 0x1000) {
n = 0x1000;
@@ -6861,8 +7337,12 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
return a;
}
- if (aExp == 0 && aSig == 0)
- return a;
+ if (aExp == 0) {
+ if (aSig == 0) {
+ return a;
+ }
+ aExp++;
+ }
if (n > 0x10000) {
n = 0x10000;
@@ -6891,10 +7371,13 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM )
}
return a;
}
- if ( aExp != 0 )
+ if (aExp != 0) {
aSig0 |= LIT64( 0x0001000000000000 );
- else if ( aSig0 == 0 && aSig1 == 0 )
+ } else if (aSig0 == 0 && aSig1 == 0) {
return a;
+ } else {
+ aExp++;
+ }
if (n > 0x10000) {
n = 0x10000;
diff --git a/gdb-xml/aarch64-fpu.xml b/gdb-xml/aarch64-fpu.xml
new file mode 100644
index 0000000000..997197e5e5
--- /dev/null
+++ b/gdb-xml/aarch64-fpu.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009-2012 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.fpu">
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v2u" type="uint64" count="2"/>
+ <vector id="v2i" type="int64" count="2"/>
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4u" type="uint32" count="4"/>
+ <vector id="v4i" type="int32" count="4"/>
+ <vector id="v8u" type="uint16" count="8"/>
+ <vector id="v8i" type="int16" count="8"/>
+ <vector id="v16u" type="uint8" count="16"/>
+ <vector id="v16i" type="int8" count="16"/>
+ <vector id="v1u" type="uint128" count="1"/>
+ <vector id="v1i" type="int128" count="1"/>
+ <union id="vnd">
+ <field name="f" type="v2d"/>
+ <field name="u" type="v2u"/>
+ <field name="s" type="v2i"/>
+ </union>
+ <union id="vns">
+ <field name="f" type="v4f"/>
+ <field name="u" type="v4u"/>
+ <field name="s" type="v4i"/>
+ </union>
+ <union id="vnh">
+ <field name="u" type="v8u"/>
+ <field name="s" type="v8i"/>
+ </union>
+ <union id="vnb">
+ <field name="u" type="v16u"/>
+ <field name="s" type="v16i"/>
+ </union>
+ <union id="vnq">
+ <field name="u" type="v1u"/>
+ <field name="s" type="v1i"/>
+ </union>
+ <union id="aarch64v">
+ <field name="d" type="vnd"/>
+ <field name="s" type="vns"/>
+ <field name="h" type="vnh"/>
+ <field name="b" type="vnb"/>
+ <field name="q" type="vnq"/>
+ </union>
+ <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
+ <reg name="v1" bitsize="128" type="aarch64v" />
+ <reg name="v2" bitsize="128" type="aarch64v" />
+ <reg name="v3" bitsize="128" type="aarch64v" />
+ <reg name="v4" bitsize="128" type="aarch64v" />
+ <reg name="v5" bitsize="128" type="aarch64v" />
+ <reg name="v6" bitsize="128" type="aarch64v" />
+ <reg name="v7" bitsize="128" type="aarch64v" />
+ <reg name="v8" bitsize="128" type="aarch64v" />
+ <reg name="v9" bitsize="128" type="aarch64v" />
+ <reg name="v10" bitsize="128" type="aarch64v"/>
+ <reg name="v11" bitsize="128" type="aarch64v"/>
+ <reg name="v12" bitsize="128" type="aarch64v"/>
+ <reg name="v13" bitsize="128" type="aarch64v"/>
+ <reg name="v14" bitsize="128" type="aarch64v"/>
+ <reg name="v15" bitsize="128" type="aarch64v"/>
+ <reg name="v16" bitsize="128" type="aarch64v"/>
+ <reg name="v17" bitsize="128" type="aarch64v"/>
+ <reg name="v18" bitsize="128" type="aarch64v"/>
+ <reg name="v19" bitsize="128" type="aarch64v"/>
+ <reg name="v20" bitsize="128" type="aarch64v"/>
+ <reg name="v21" bitsize="128" type="aarch64v"/>
+ <reg name="v22" bitsize="128" type="aarch64v"/>
+ <reg name="v23" bitsize="128" type="aarch64v"/>
+ <reg name="v24" bitsize="128" type="aarch64v"/>
+ <reg name="v25" bitsize="128" type="aarch64v"/>
+ <reg name="v26" bitsize="128" type="aarch64v"/>
+ <reg name="v27" bitsize="128" type="aarch64v"/>
+ <reg name="v28" bitsize="128" type="aarch64v"/>
+ <reg name="v29" bitsize="128" type="aarch64v"/>
+ <reg name="v30" bitsize="128" type="aarch64v"/>
+ <reg name="v31" bitsize="128" type="aarch64v"/>
+ <reg name="fpsr" bitsize="32"/>
+ <reg name="fpcr" bitsize="32"/>
+</feature>
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ebe8e78bb9..f3fc514427 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -35,6 +35,11 @@ STEXI
@item commit
@findex commit
Commit changes to the disk images (if -snapshot is used) or backing files.
+If the backing file is smaller than the snapshot, then the backing file will be
+resized to be the same size as the snapshot. If the snapshot is smaller than
+the backing file, the backing file will not be truncated. If you want the
+backing file to match the size of the smaller snapshot, you can safely truncate
+it yourself once the commit operation successfully completes.
ETEXI
{
@@ -1243,6 +1248,34 @@ STEXI
Remove host network device.
ETEXI
+ {
+ .name = "object_add",
+ .args_type = "object:O",
+ .params = "[qom-type=]type,id=str[,prop=value][,...]",
+ .help = "create QOM object",
+ .mhandler.cmd = hmp_object_add,
+ },
+
+STEXI
+@item object_add
+@findex object_add
+Create QOM object.
+ETEXI
+
+ {
+ .name = "object_del",
+ .args_type = "id:s",
+ .params = "id",
+ .help = "destroy QOM object",
+ .mhandler.cmd = hmp_object_del,
+ },
+
+STEXI
+@item object_del
+@findex object_del
+Destroy QOM object.
+ETEXI
+
#ifdef CONFIG_SLIRP
{
.name = "hostfwd_add",
@@ -1620,6 +1653,19 @@ Executes a qemu-io command on the given block device.
ETEXI
{
+ .name = "cpu-add",
+ .args_type = "id:i",
+ .params = "id",
+ .help = "add cpu",
+ .mhandler.cmd = hmp_cpu_add,
+ },
+
+STEXI
+@item cpu-add @var{id}
+Add CPU with id @var{id}
+ETEXI
+
+ {
.name = "info",
.args_type = "item:s?",
.params = "[subcommand]",
diff --git a/hmp.c b/hmp.c
index 32ee285a1e..1af0809305 100644
--- a/hmp.c
+++ b/hmp.c
@@ -21,6 +21,7 @@
#include "qmp-commands.h"
#include "qemu/sockets.h"
#include "monitor/monitor.h"
+#include "qapi/opts-visitor.h"
#include "ui/console.h"
#include "block/qapi.h"
#include "qemu-io.h"
@@ -870,7 +871,7 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict)
const char *password = qdict_get_str(qdict, "password");
Error *errp = NULL;
- qmp_block_passwd(device, password, &errp);
+ qmp_block_passwd(true, device, false, NULL, password, &errp);
hmp_handle_error(mon, &errp);
}
@@ -892,7 +893,7 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
int64_t size = qdict_get_int(qdict, "size");
Error *errp = NULL;
- qmp_block_resize(device, size, &errp);
+ qmp_block_resize(true, device, false, NULL, size, &errp);
hmp_handle_error(mon, &errp);
}
@@ -971,7 +972,9 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
}
mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
- qmp_blockdev_snapshot_sync(device, filename, !!format, format,
+ qmp_blockdev_snapshot_sync(true, device, false, NULL,
+ filename, false, NULL,
+ !!format, format,
true, mode, &errp);
hmp_handle_error(mon, &errp);
}
@@ -1091,11 +1094,11 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
-static void hmp_change_read_arg(Monitor *mon, const char *password,
- void *opaque)
+static void hmp_change_read_arg(void *opaque, const char *password,
+ void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
- monitor_read_command(mon, 1);
+ monitor_read_command(opaque, 1);
}
void hmp_change(Monitor *mon, const QDict *qdict)
@@ -1354,6 +1357,63 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
+void hmp_object_add(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ QemuOpts *opts;
+ char *type = NULL;
+ char *id = NULL;
+ void *dummy = NULL;
+ OptsVisitor *ov;
+ QDict *pdict;
+
+ opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
+ if (err) {
+ goto out;
+ }
+
+ ov = opts_visitor_new(opts);
+ pdict = qdict_clone_shallow(qdict);
+
+ visit_start_struct(opts_get_visitor(ov), &dummy, NULL, NULL, 0, &err);
+ if (err) {
+ goto out_clean;
+ }
+
+ qdict_del(pdict, "qom-type");
+ visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
+ if (err) {
+ goto out_clean;
+ }
+
+ qdict_del(pdict, "id");
+ visit_type_str(opts_get_visitor(ov), &id, "id", &err);
+ if (err) {
+ goto out_clean;
+ }
+
+ object_add(type, id, pdict, opts_get_visitor(ov), &err);
+ if (err) {
+ goto out_clean;
+ }
+ visit_end_struct(opts_get_visitor(ov), &err);
+ if (err) {
+ qmp_object_del(id, NULL);
+ }
+
+out_clean:
+ opts_visitor_cleanup(ov);
+
+ QDECREF(pdict);
+ qemu_opts_del(opts);
+ g_free(id);
+ g_free(type);
+ g_free(dummy);
+
+out:
+ hmp_handle_error(mon, &err);
+}
+
void hmp_getfd(Monitor *mon, const QDict *qdict)
{
const char *fdname = qdict_get_str(qdict, "fdname");
@@ -1525,6 +1585,16 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &errp);
}
+void hmp_cpu_add(Monitor *mon, const QDict *qdict)
+{
+ int cpuid;
+ Error *err = NULL;
+
+ cpuid = qdict_get_int(qdict, "id");
+ qmp_cpu_add(cpuid, &err);
+ hmp_handle_error(mon, &err);
+}
+
void hmp_chardev_add(Monitor *mon, const QDict *qdict)
{
const char *args = qdict_get_str(qdict, "args");
@@ -1564,3 +1634,12 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
+
+void hmp_object_del(Monitor *mon, const QDict *qdict)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ Error *err = NULL;
+
+ qmp_object_del(id, &err);
+ hmp_handle_error(mon, &err);
+}
diff --git a/hmp.h b/hmp.h
index 54cf71fb94..ed58f0ea41 100644
--- a/hmp.h
+++ b/hmp.h
@@ -89,5 +89,8 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
void hmp_qemu_io(Monitor *mon, const QDict *qdict);
+void hmp_cpu_add(Monitor *mon, const QDict *qdict);
+void hmp_object_add(Monitor *mon, const QDict *qdict);
+void hmp_object_del(Monitor *mon, const QDict *qdict);
#endif
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index f0ffbe8c0d..15a4983eee 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -41,15 +41,16 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
g_free(cfg);
}
-static int virtio_9p_device_init(VirtIODevice *vdev)
+static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
{
- V9fsState *s = VIRTIO_9P(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ V9fsState *s = VIRTIO_9P(dev);
int i, len;
struct stat stat;
FsDriverEntry *fse;
V9fsPath path;
- virtio_init(VIRTIO_DEVICE(s), "virtio-9p", VIRTIO_ID_9P,
+ virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P,
sizeof(struct virtio_9p_config) + MAX_TAG_LEN);
/* initialize pdu allocator */
@@ -67,16 +68,16 @@ static int virtio_9p_device_init(VirtIODevice *vdev)
if (!fse) {
/* We don't have a fsdev identified by fsdev_id */
- fprintf(stderr, "Virtio-9p device couldn't find fsdev with the "
- "id = %s\n",
- s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
+ error_setg(errp, "Virtio-9p device couldn't find fsdev with the "
+ "id = %s",
+ s->fsconf.fsdev_id ? s->fsconf.fsdev_id : "NULL");
goto out;
}
if (!s->fsconf.tag) {
/* we haven't specified a mount_tag */
- fprintf(stderr, "fsdev with id %s needs mount_tag arguments\n",
- s->fsconf.fsdev_id);
+ error_setg(errp, "fsdev with id %s needs mount_tag arguments",
+ s->fsconf.fsdev_id);
goto out;
}
@@ -85,8 +86,8 @@ static int virtio_9p_device_init(VirtIODevice *vdev)
s->ctx.exops.get_st_gen = NULL;
len = strlen(s->fsconf.tag);
if (len > MAX_TAG_LEN - 1) {
- fprintf(stderr, "mount tag '%s' (%d bytes) is longer than "
- "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
+ error_setg(errp, "mount tag '%s' (%d bytes) is longer than "
+ "maximum (%d bytes)", s->fsconf.tag, len, MAX_TAG_LEN - 1);
goto out;
}
@@ -99,12 +100,12 @@ static int virtio_9p_device_init(VirtIODevice *vdev)
qemu_co_rwlock_init(&s->rename_lock);
if (s->ops->init(&s->ctx) < 0) {
- fprintf(stderr, "Virtio-9p Failed to initialize fs-driver with id:%s"
- " and export path:%s\n", s->fsconf.fsdev_id, s->ctx.fs_root);
+ error_setg(errp, "Virtio-9p Failed to initialize fs-driver with id:%s"
+ " and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
goto out;
}
if (v9fs_init_worker_threads() < 0) {
- fprintf(stderr, "worker thread initialization failed\n");
+ error_setg(errp, "worker thread initialization failed");
goto out;
}
@@ -114,28 +115,25 @@ static int virtio_9p_device_init(VirtIODevice *vdev)
* use co-routines here.
*/
if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
- fprintf(stderr,
- "error in converting name to path %s", strerror(errno));
+ error_setg(errp,
+ "error in converting name to path %s", strerror(errno));
goto out;
}
if (s->ops->lstat(&s->ctx, &path, &stat)) {
- fprintf(stderr, "share path %s does not exist\n", fse->path);
+ error_setg(errp, "share path %s does not exist", fse->path);
goto out;
} else if (!S_ISDIR(stat.st_mode)) {
- fprintf(stderr, "share path %s is not a directory\n", fse->path);
+ error_setg(errp, "share path %s is not a directory", fse->path);
goto out;
}
v9fs_path_free(&path);
- return 0;
+ return;
out:
g_free(s->ctx.fs_root);
g_free(s->tag);
virtio_cleanup(vdev);
v9fs_path_free(&path);
-
- return -1;
-
}
/* virtio-9p device */
@@ -149,9 +147,10 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
dc->props = virtio_9p_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- vdc->init = virtio_9p_device_init;
+ vdc->realize = virtio_9p_device_realize;
vdc->get_features = virtio_9p_get_features;
vdc->get_config = virtio_9p_get_config;
}
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 58308a3406..79414b44c7 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -662,3 +662,21 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
return val;
}
+
+void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
+{
+ int sci_level, pm1a_sts;
+
+ pm1a_sts = acpi_pm1_evt_get_sts(regs);
+
+ sci_level = ((pm1a_sts &
+ regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) ||
+ ((regs->gpe.sts[0] & regs->gpe.en[0]) != 0);
+
+ qemu_set_irq(irq, sci_level);
+
+ /* schedule a timer interruption if needed */
+ acpi_pm_tmr_update(regs,
+ (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
+ !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
+}
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 7e0429e0f9..30f0df8713 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -44,29 +44,10 @@ do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
#define ICH9_DEBUG(fmt, ...) do { } while (0)
#endif
-static void pm_update_sci(ICH9LPCPMRegs *pm)
-{
- int sci_level, pm1a_sts;
-
- pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
-
- sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
- (ACPI_BITMASK_RT_CLOCK_ENABLE |
- ACPI_BITMASK_POWER_BUTTON_ENABLE |
- ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
- ACPI_BITMASK_TIMER_ENABLE)) != 0);
- qemu_set_irq(pm->irq, sci_level);
-
- /* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&pm->acpi_regs,
- (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
- !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
-}
-
static void ich9_pm_update_sci_fn(ACPIREGS *regs)
{
ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
- pm_update_sci(pm);
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
}
static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
@@ -80,6 +61,7 @@ static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
{
ICH9LPCPMRegs *pm = opaque;
acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
}
static const MemoryRegionOps ich9_gpe_ops = {
@@ -193,7 +175,7 @@ static void pm_reset(void *opaque)
pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
}
- pm_update_sci(pm);
+ acpi_update_sci(&pm->acpi_regs, pm->irq);
}
static void pm_powerdown_req(Notifier *n, void *opaque)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index 93849c8d36..20353b983e 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -112,29 +112,10 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
#define ACPI_ENABLE 0xf1
#define ACPI_DISABLE 0xf0
-static void pm_update_sci(PIIX4PMState *s)
-{
- int sci_level, pmsts;
-
- pmsts = acpi_pm1_evt_get_sts(&s->ar);
- sci_level = (((pmsts & s->ar.pm1.evt.en) &
- (ACPI_BITMASK_RT_CLOCK_ENABLE |
- ACPI_BITMASK_POWER_BUTTON_ENABLE |
- ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
- ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
- (((s->ar.gpe.sts[0] & s->ar.gpe.en[0]) &
- (PIIX4_PCI_HOTPLUG_STATUS | PIIX4_CPU_HOTPLUG_STATUS)) != 0);
-
- qemu_set_irq(s->irq, sci_level);
- /* schedule a timer interruption if needed */
- acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
- !(pmsts & ACPI_BITMASK_TIMER_STATUS));
-}
-
static void pm_tmr_timer(ACPIREGS *ar)
{
PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
- pm_update_sci(s);
+ acpi_update_sci(&s->ar, s->irq);
}
static void apm_ctrl_changed(uint32_t val, void *arg)
@@ -544,9 +525,13 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
k->revision = 0x03;
k->class_id = PCI_CLASS_BRIDGE_OTHER;
dc->desc = "PM";
- dc->no_user = 1;
dc->vmsd = &vmstate_acpi;
dc->props = piix4_pm_properties;
+ /*
+ * Reason: part of PIIX4 southbridge, needs to be wired up,
+ * e.g. by mips_malta_init()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo piix4_pm_info = {
@@ -578,7 +563,7 @@ static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
PIIX4PMState *s = opaque;
acpi_gpe_ioport_writeb(&s->ar, addr, val);
- pm_update_sci(s);
+ acpi_update_sci(&s->ar, s->irq);
PIIX4_DPRINTF("gpe write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, val);
}
@@ -694,7 +679,7 @@ static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
} else {
g->sts[cpu_id / 8] &= ~(1 << (cpu_id % 8));
}
- pm_update_sci(s);
+ acpi_update_sci(&s->ar, s->irq);
}
static void piix4_cpu_added_req(Notifier *n, void *opaque)
@@ -768,7 +753,7 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
disable_device(s, slot);
}
- pm_update_sci(s);
+ acpi_update_sci(&s->ar, s->irq);
return 0;
}
diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c
index 59e1bb8388..71a5a37fdc 100644
--- a/hw/alpha/typhoon.c
+++ b/hw/alpha/typhoon.c
@@ -934,11 +934,9 @@ static int typhoon_pcihost_init(SysBusDevice *dev)
static void typhoon_pcihost_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = typhoon_pcihost_init;
- dc->no_user = 1;
}
static const TypeInfo typhoon_pcihost_info = {
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 78b56149b6..6088e53653 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,7 +1,10 @@
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
+obj-$(CONFIG_DIGIC) += digic_boards.o
obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
obj-y += 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-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
+obj-$(CONFIG_DIGIC) += digic.o
obj-y += omap1.o omap2.o strongarm.o
+obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
new file mode 100644
index 0000000000..4658e19504
--- /dev/null
+++ b/hw/arm/allwinner-a10.c
@@ -0,0 +1,103 @@
+/*
+ * Allwinner A10 SoC emulation
+ *
+ * Copyright (C) 2013 Li Guang
+ * Written by Li Guang <lig.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "hw/arm/allwinner-a10.h"
+
+static void aw_a10_init(Object *obj)
+{
+ AwA10State *s = AW_A10(obj);
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU);
+ object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
+
+ object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC);
+ qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default());
+
+ object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
+ qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
+}
+
+static void aw_a10_realize(DeviceState *dev, Error **errp)
+{
+ AwA10State *s = AW_A10(dev);
+ SysBusDevice *sysbusdev;
+ uint8_t i;
+ qemu_irq fiq, irq;
+ Error *err = NULL;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ);
+ fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ);
+
+ object_property_set_bool(OBJECT(&s->intc), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbusdev = SYS_BUS_DEVICE(&s->intc);
+ sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE);
+ sysbus_connect_irq(sysbusdev, 0, irq);
+ sysbus_connect_irq(sysbusdev, 1, fiq);
+ for (i = 0; i < AW_A10_PIC_INT_NR; i++) {
+ s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i);
+ }
+
+ object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbusdev = SYS_BUS_DEVICE(&s->timer);
+ sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE);
+ sysbus_connect_irq(sysbusdev, 0, s->irq[22]);
+ sysbus_connect_irq(sysbusdev, 1, s->irq[23]);
+ sysbus_connect_irq(sysbusdev, 2, s->irq[24]);
+ sysbus_connect_irq(sysbusdev, 3, s->irq[25]);
+ sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
+ sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
+
+ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
+ 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
+}
+
+static void aw_a10_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = aw_a10_realize;
+}
+
+static const TypeInfo aw_a10_type_info = {
+ .name = TYPE_AW_A10,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(AwA10State),
+ .instance_init = aw_a10_init,
+ .class_init = aw_a10_class_init,
+};
+
+static void aw_a10_register_types(void)
+{
+ type_register_static(&aw_a10_type_info);
+}
+
+type_init(aw_a10_register_types)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 55d552f3a8..1c1b0e5258 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -17,18 +17,55 @@
#include "sysemu/device_tree.h"
#include "qemu/config-file.h"
+/* Kernel boot protocol is specified in the kernel docs
+ * Documentation/arm/Booting and Documentation/arm64/booting.txt
+ * They have different preferred image load offsets from system RAM base.
+ */
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
+#define KERNEL64_LOAD_ADDR 0x00080000
+
+typedef enum {
+ FIXUP_NONE = 0, /* do nothing */
+ FIXUP_TERMINATOR, /* end of insns */
+ FIXUP_BOARDID, /* overwrite with board ID number */
+ FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
+ FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
+ FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
+ FIXUP_BOOTREG, /* overwrite with boot register address */
+ FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
+ FIXUP_MAX,
+} FixupType;
+
+typedef struct ARMInsnFixup {
+ uint32_t insn;
+ FixupType fixup;
+} ARMInsnFixup;
+
+static const ARMInsnFixup bootloader_aarch64[] = {
+ { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
+ { 0xaa1f03e1 }, /* mov x1, xzr */
+ { 0xaa1f03e2 }, /* mov x2, xzr */
+ { 0xaa1f03e3 }, /* mov x3, xzr */
+ { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
+ { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
+ { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */
+ { 0 }, /* .word @DTB Higher 32-bits */
+ { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */
+ { 0 }, /* .word @Kernel Entry Higher 32-bits */
+ { 0, FIXUP_TERMINATOR }
+};
/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
-static uint32_t bootloader[] = {
- 0xe3a00000, /* mov r0, #0 */
- 0xe59f1004, /* ldr r1, [pc, #4] */
- 0xe59f2004, /* ldr r2, [pc, #4] */
- 0xe59ff004, /* ldr pc, [pc, #4] */
- 0, /* Board ID */
- 0, /* Address of kernel args. Set by integratorcp_init. */
- 0 /* Kernel entry point. Set by integratorcp_init. */
+static const ARMInsnFixup bootloader[] = {
+ { 0xe3a00000 }, /* mov r0, #0 */
+ { 0xe59f1004 }, /* ldr r1, [pc, #4] */
+ { 0xe59f2004 }, /* ldr r2, [pc, #4] */
+ { 0xe59ff004 }, /* ldr pc, [pc, #4] */
+ { 0, FIXUP_BOARDID },
+ { 0, FIXUP_ARGPTR },
+ { 0, FIXUP_ENTRYPOINT },
+ { 0, FIXUP_TERMINATOR }
};
/* Handling for secondary CPU boot in a multicore system.
@@ -48,39 +85,83 @@ static uint32_t bootloader[] = {
#define DSB_INSN 0xf57ff04f
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
-static uint32_t smpboot[] = {
- 0xe59f2028, /* ldr r2, gic_cpu_if */
- 0xe59f0028, /* ldr r0, startaddr */
- 0xe3a01001, /* mov r1, #1 */
- 0xe5821000, /* str r1, [r2] - set GICC_CTLR.Enable */
- 0xe3a010ff, /* mov r1, #0xff */
- 0xe5821004, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
- DSB_INSN, /* dsb */
- 0xe320f003, /* wfi */
- 0xe5901000, /* ldr r1, [r0] */
- 0xe1110001, /* tst r1, r1 */
- 0x0afffffb, /* beq <wfi> */
- 0xe12fff11, /* bx r1 */
- 0, /* gic_cpu_if: base address of GIC CPU interface */
- 0 /* bootreg: Boot register address is held here */
+static const ARMInsnFixup smpboot[] = {
+ { 0xe59f2028 }, /* ldr r2, gic_cpu_if */
+ { 0xe59f0028 }, /* ldr r0, bootreg_addr */
+ { 0xe3a01001 }, /* mov r1, #1 */
+ { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
+ { 0xe3a010ff }, /* mov r1, #0xff */
+ { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
+ { 0, FIXUP_DSB }, /* dsb */
+ { 0xe320f003 }, /* wfi */
+ { 0xe5901000 }, /* ldr r1, [r0] */
+ { 0xe1110001 }, /* tst r1, r1 */
+ { 0x0afffffb }, /* beq <wfi> */
+ { 0xe12fff11 }, /* bx r1 */
+ { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
+ { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
+ { 0, FIXUP_TERMINATOR }
};
+static void write_bootloader(const char *name, hwaddr addr,
+ const ARMInsnFixup *insns, uint32_t *fixupcontext)
+{
+ /* Fix up the specified bootloader fragment and write it into
+ * guest memory using rom_add_blob_fixed(). fixupcontext is
+ * an array giving the values to write in for the fixup types
+ * which write a value into the code array.
+ */
+ int i, len;
+ uint32_t *code;
+
+ len = 0;
+ while (insns[len].fixup != FIXUP_TERMINATOR) {
+ len++;
+ }
+
+ code = g_new0(uint32_t, len);
+
+ for (i = 0; i < len; i++) {
+ uint32_t insn = insns[i].insn;
+ FixupType fixup = insns[i].fixup;
+
+ switch (fixup) {
+ case FIXUP_NONE:
+ break;
+ case FIXUP_BOARDID:
+ case FIXUP_ARGPTR:
+ case FIXUP_ENTRYPOINT:
+ case FIXUP_GIC_CPU_IF:
+ case FIXUP_BOOTREG:
+ case FIXUP_DSB:
+ insn = fixupcontext[fixup];
+ break;
+ default:
+ abort();
+ }
+ code[i] = tswap32(insn);
+ }
+
+ rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr);
+
+ g_free(code);
+}
+
static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
- int n;
- smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
- smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
- for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
- /* Replace DSB with the pre-v7 DSB if necessary. */
- if (!arm_feature(&cpu->env, ARM_FEATURE_V7) &&
- smpboot[n] == DSB_INSN) {
- smpboot[n] = CP15_DSB_INSN;
- }
- smpboot[n] = tswap32(smpboot[n]);
+ uint32_t fixupcontext[FIXUP_MAX];
+
+ fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
+ fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
+ if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
+ fixupcontext[FIXUP_DSB] = DSB_INSN;
+ } else {
+ fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
}
- rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
- info->smp_loader_start);
+
+ write_bootloader("smpboot", info->smp_loader_start,
+ smpboot, fixupcontext);
}
static void default_reset_secondary(ARMCPU *cpu,
@@ -254,8 +335,8 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
}
}
- acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells");
- scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells");
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells");
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells");
if (acells == 0 || scells == 0) {
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
goto fail;
@@ -270,17 +351,17 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
goto fail;
}
- rc = qemu_devtree_setprop_sized_cells(fdt, "/memory", "reg",
- acells, binfo->loader_start,
- scells, binfo->ram_size);
+ rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg",
+ acells, binfo->loader_start,
+ scells, binfo->ram_size);
if (rc < 0) {
fprintf(stderr, "couldn't set /memory/reg\n");
goto fail;
}
if (binfo->kernel_cmdline && *binfo->kernel_cmdline) {
- rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
- binfo->kernel_cmdline);
+ rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
+ binfo->kernel_cmdline);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/bootargs\n");
goto fail;
@@ -288,15 +369,15 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
}
if (binfo->initrd_size) {
- rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
- binfo->initrd_start);
+ rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ binfo->initrd_start);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
goto fail;
}
- rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
- binfo->initrd_start + binfo->initrd_size);
+ rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ binfo->initrd_start + binfo->initrd_size);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
goto fail;
@@ -307,7 +388,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo)
binfo->modify_dtb(binfo, fdt);
}
- qemu_devtree_dumpdtb(fdt, size);
+ qemu_fdt_dumpdtb(fdt, size);
cpu_physical_memory_write(addr, fdt, size);
@@ -334,7 +415,12 @@ static void do_cpu_reset(void *opaque)
env->thumb = info->entry & 1;
} else {
if (CPU(cpu) == first_cpu) {
- env->regs[15] = info->loader_start;
+ if (env->aarch64) {
+ env->pc = info->loader_start;
+ } else {
+ env->regs[15] = info->loader_start;
+ }
+
if (!info->dtb_filename) {
if (old_param) {
set_kernel_args_old(info);
@@ -354,11 +440,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
CPUState *cs = CPU(cpu);
int kernel_size;
int initrd_size;
- int n;
int is_linux = 0;
uint64_t elf_entry;
- hwaddr entry;
+ hwaddr entry, kernel_load_offset;
int big_endian;
+ static const ARMInsnFixup *primary_loader;
/* Load the kernel. */
if (!info->kernel_filename) {
@@ -368,6 +454,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
return;
}
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ primary_loader = bootloader_aarch64;
+ kernel_load_offset = KERNEL64_LOAD_ADDR;
+ } else {
+ primary_loader = bootloader;
+ kernel_load_offset = KERNEL_LOAD_ADDR;
+ }
+
info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
if (!info->secondary_cpu_reset_hook) {
@@ -408,9 +502,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
&is_linux);
}
if (kernel_size < 0) {
- entry = info->loader_start + KERNEL_LOAD_ADDR;
+ entry = info->loader_start + kernel_load_offset;
kernel_size = load_image_targphys(info->kernel_filename, entry,
- info->ram_size - KERNEL_LOAD_ADDR);
+ info->ram_size - kernel_load_offset);
is_linux = 1;
}
if (kernel_size < 0) {
@@ -420,6 +514,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->entry = entry;
if (is_linux) {
+ uint32_t fixupcontext[FIXUP_MAX];
+
if (info->initrd_filename) {
initrd_size = load_ramdisk(info->initrd_filename,
info->initrd_start,
@@ -441,7 +537,7 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
}
info->initrd_size = initrd_size;
- bootloader[4] = info->board_id;
+ fixupcontext[FIXUP_BOARDID] = info->board_id;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
@@ -456,9 +552,9 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
if (load_dtb(dtb_start, info)) {
exit(1);
}
- bootloader[5] = dtb_start;
+ fixupcontext[FIXUP_ARGPTR] = dtb_start;
} else {
- bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+ fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR;
if (info->ram_size >= (1ULL << 32)) {
fprintf(stderr, "qemu: RAM size must be less than 4GB to boot"
" Linux kernel using ATAGS (try passing a device tree"
@@ -466,12 +562,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
exit(1);
}
}
- bootloader[6] = entry;
- for (n = 0; n < sizeof(bootloader) / 4; n++) {
- bootloader[n] = tswap32(bootloader[n]);
- }
- rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
- info->loader_start);
+ fixupcontext[FIXUP_ENTRYPOINT] = entry;
+
+ write_bootloader("bootloader", info->loader_start,
+ primary_loader, fixupcontext);
+
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
}
diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
new file mode 100644
index 0000000000..3fcb6d22f5
--- /dev/null
+++ b/hw/arm/cubieboard.c
@@ -0,0 +1,69 @@
+/*
+ * cubieboard emulation
+ *
+ * Copyright (C) 2013 Li Guang
+ * Written by Li Guang <lig.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "hw/boards.h"
+#include "hw/arm/allwinner-a10.h"
+
+static struct arm_boot_info cubieboard_binfo = {
+ .loader_start = AW_A10_SDRAM_BASE,
+ .board_id = 0x1008,
+};
+
+typedef struct CubieBoardState {
+ AwA10State *a10;
+ MemoryRegion sdram;
+} CubieBoardState;
+
+static void cubieboard_init(QEMUMachineInitArgs *args)
+{
+ CubieBoardState *s = g_new(CubieBoardState, 1);
+ Error *err = NULL;
+
+ s->a10 = AW_A10(object_new(TYPE_AW_A10));
+ object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
+ if (err != NULL) {
+ error_report("Couldn't realize Allwinner A10: %s\n",
+ error_get_pretty(err));
+ exit(1);
+ }
+
+ memory_region_init_ram(&s->sdram, NULL, "cubieboard.ram", args->ram_size);
+ vmstate_register_ram_global(&s->sdram);
+ memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE,
+ &s->sdram);
+
+ cubieboard_binfo.ram_size = args->ram_size;
+ cubieboard_binfo.kernel_filename = args->kernel_filename;
+ cubieboard_binfo.kernel_cmdline = args->kernel_cmdline;
+ arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
+}
+
+static QEMUMachine cubieboard_machine = {
+ .name = "cubieboard",
+ .desc = "cubietech cubieboard",
+ .init = cubieboard_init,
+};
+
+
+static void cubieboard_machine_init(void)
+{
+ qemu_register_machine(&cubieboard_machine);
+}
+
+machine_init(cubieboard_machine_init)
diff --git a/hw/arm/digic.c b/hw/arm/digic.c
new file mode 100644
index 0000000000..ec8c330602
--- /dev/null
+++ b/hw/arm/digic.c
@@ -0,0 +1,115 @@
+/*
+ * QEMU model of the Canon DIGIC SoC.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This model is based on reverse engineering efforts
+ * made by CHDK (http://chdk.wikia.com) and
+ * Magic Lantern (http://www.magiclantern.fm) projects
+ * contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hw/arm/digic.h"
+
+#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100)
+
+#define DIGIC_UART_BASE 0xc0800000
+
+static void digic_init(Object *obj)
+{
+ DigicState *s = DIGIC(obj);
+ DeviceState *dev;
+ int i;
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU);
+ object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL);
+
+ for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
+#define DIGIC_TIMER_NAME_MLEN 11
+ char name[DIGIC_TIMER_NAME_MLEN];
+
+ object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER);
+ dev = DEVICE(&s->timer[i]);
+ qdev_set_parent_bus(dev, sysbus_get_default());
+ snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i);
+ object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL);
+ }
+
+ object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART);
+ dev = DEVICE(&s->uart);
+ qdev_set_parent_bus(dev, sysbus_get_default());
+ object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL);
+}
+
+static void digic_realize(DeviceState *dev, Error **errp)
+{
+ DigicState *s = DIGIC(dev);
+ Error *err = NULL;
+ SysBusDevice *sbd;
+ int i;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ for (i = 0; i < DIGIC4_NB_TIMERS; i++) {
+ object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sbd = SYS_BUS_DEVICE(&s->timer[i]);
+ sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i));
+ }
+
+ object_property_set_bool(OBJECT(&s->uart), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sbd = SYS_BUS_DEVICE(&s->uart);
+ sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE);
+}
+
+static void digic_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = digic_realize;
+}
+
+static const TypeInfo digic_type_info = {
+ .name = TYPE_DIGIC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(DigicState),
+ .instance_init = digic_init,
+ .class_init = digic_class_init,
+};
+
+static void digic_register_types(void)
+{
+ type_register_static(&digic_type_info);
+}
+
+type_init(digic_register_types)
diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c
new file mode 100644
index 0000000000..32fc30a69d
--- /dev/null
+++ b/hw/arm/digic_boards.c
@@ -0,0 +1,162 @@
+/*
+ * QEMU model of the Canon DIGIC boards (cameras indeed :).
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This model is based on reverse engineering efforts
+ * made by CHDK (http://chdk.wikia.com) and
+ * Magic Lantern (http://www.magiclantern.fm) projects
+ * contributors.
+ *
+ * See docs here:
+ * http://magiclantern.wikia.com/wiki/Register_Map
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
+#include "hw/arm/digic.h"
+#include "hw/block/flash.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+
+#define DIGIC4_ROM0_BASE 0xf0000000
+#define DIGIC4_ROM1_BASE 0xf8000000
+#define DIGIC4_ROM_MAX_SIZE 0x08000000
+
+typedef struct DigicBoardState {
+ DigicState *digic;
+ MemoryRegion ram;
+} DigicBoardState;
+
+typedef struct DigicBoard {
+ hwaddr ram_size;
+ void (*add_rom0)(DigicBoardState *, hwaddr, const char *);
+ const char *rom0_def_filename;
+ void (*add_rom1)(DigicBoardState *, hwaddr, const char *);
+ const char *rom1_def_filename;
+} DigicBoard;
+
+static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size)
+{
+ memory_region_init_ram(&s->ram, NULL, "ram", ram_size);
+ memory_region_add_subregion(get_system_memory(), 0, &s->ram);
+ vmstate_register_ram_global(&s->ram);
+}
+
+static void digic4_board_init(DigicBoard *board)
+{
+ Error *err = NULL;
+
+ DigicBoardState *s = g_new(DigicBoardState, 1);
+
+ s->digic = DIGIC(object_new(TYPE_DIGIC));
+ object_property_set_bool(OBJECT(s->digic), true, "realized", &err);
+ if (err != NULL) {
+ error_report("Couldn't realize DIGIC SoC: %s\n",
+ error_get_pretty(err));
+ exit(1);
+ }
+
+ digic4_board_setup_ram(s, board->ram_size);
+
+ if (board->add_rom0) {
+ board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename);
+ }
+
+ if (board->add_rom1) {
+ board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename);
+ }
+}
+
+static void digic_load_rom(DigicBoardState *s, hwaddr addr,
+ hwaddr max_size, const char *def_filename)
+{
+ target_long rom_size;
+ const char *filename;
+
+ if (qtest_enabled()) {
+ /* qtest runs no code so don't attempt a ROM load which
+ * could fail and result in a spurious test failure.
+ */
+ return;
+ }
+
+ if (bios_name) {
+ filename = bios_name;
+ } else {
+ filename = def_filename;
+ }
+
+ if (filename) {
+ char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename);
+
+ if (!fn) {
+ error_report("Couldn't find rom image '%s'.\n", filename);
+ exit(1);
+ }
+
+ rom_size = load_image_targphys(fn, addr, max_size);
+ if (rom_size < 0 || rom_size > max_size) {
+ error_report("Couldn't load rom image '%s'.\n", filename);
+ exit(1);
+ }
+ }
+}
+
+/*
+ * Samsung K8P3215UQB
+ * 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory
+ */
+static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr,
+ const char *def_filename)
+{
+#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024)
+#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024)
+
+ pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE,
+ NULL, FLASH_K8P3215UQB_SECTOR_SIZE,
+ FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE,
+ DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE,
+ 4,
+ 0x00EC, 0x007E, 0x0003, 0x0001,
+ 0x0555, 0x2aa, 0);
+
+ digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename);
+}
+
+static DigicBoard digic4_board_canon_a1100 = {
+ .ram_size = 64 * 1024 * 1024,
+ .add_rom1 = digic4_add_k8p3215uqb_rom,
+ .rom1_def_filename = "canon-a1100-rom1.bin",
+};
+
+static void canon_a1100_init(QEMUMachineInitArgs *args)
+{
+ digic4_board_init(&digic4_board_canon_a1100);
+}
+
+static QEMUMachine canon_a1100 = {
+ .name = "canon-a1100",
+ .desc = "Canon PowerShot A1100 IS",
+ .init = &canon_a1100_init,
+};
+
+static void digic_register_machines(void)
+{
+ qemu_register_machine(&canon_a1100);
+}
+
+machine_init(digic_register_machines)
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index fe98ef10cb..d76a1d1f78 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -26,12 +26,13 @@
#include "hw/boards.h"
#include "sysemu/blockdev.h"
#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
-#define SMP_BOOT_ADDR 0x100
-#define SMP_BOOT_REG 0x40
-#define GIC_BASE_ADDR 0xfff10000
+#define SMP_BOOT_ADDR 0x100
+#define SMP_BOOT_REG 0x40
+#define MPCORE_PERIPHBASE 0xfff10000
-#define NIRQ_GIC 160
+#define NIRQ_GIC 160
/* Board init. */
@@ -54,7 +55,7 @@ static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
0xe1110001, /* tst r1, r1 */
0x0afffffb, /* beq <wfi> */
0xe12fff11, /* bx r1 */
- GIC_BASE_ADDR /* privbase: gic address. */
+ MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */
};
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
@@ -125,7 +126,7 @@ typedef struct {
SysBusDevice parent_obj;
/*< public >*/
- MemoryRegion *iomem;
+ MemoryRegion iomem;
uint32_t regs[NUM_REGS];
} HighbankRegsState;
@@ -154,10 +155,9 @@ static int highbank_regs_init(SysBusDevice *dev)
{
HighbankRegsState *s = HIGHBANK_REGISTERS(dev);
- s->iomem = g_new(MemoryRegion, 1);
- memory_region_init_io(s->iomem, OBJECT(s), &hb_mem_ops, s->regs,
+ memory_region_init_io(&s->iomem, OBJECT(s), &hb_mem_ops, s->regs,
"highbank_regs", 0x1000);
- sysbus_init_mmio(dev, s->iomem);
+ sysbus_init_mmio(dev, &s->iomem);
return 0;
}
@@ -229,15 +229,23 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
}
for (n = 0; n < smp_cpus; n++) {
+ ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
ARMCPU *cpu;
- cpu = cpu_arm_init(cpu_model);
- if (cpu == NULL) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ Error *err = NULL;
+
+ cpu = ARM_CPU(object_new(object_class_get_name(oc)));
+
+ object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar",
+ &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+ object_property_set_bool(OBJECT(cpu), true, "realized", &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
exit(1);
}
-
- /* This will become a QOM property eventually */
- cpu->reset_cbar = GIC_BASE_ADDR;
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
}
@@ -279,7 +287,7 @@ static void calxeda_init(QEMUMachineInitArgs *args, enum cxmachines machine)
qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
- sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR);
+ sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
}
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
index 9402c841e9..d8e075e26d 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -45,7 +45,7 @@
#define S1_STSCHG_IRQ 14
#define S1_IRQ 15
-static struct keymap map[0xE0] = {
+static const struct keymap map[0xE0] = {
[0 ... 0xDF] = { -1, -1 },
[0x1e] = {0,0}, /* a */
[0x30] = {0,1}, /* b */
@@ -75,9 +75,18 @@ static struct keymap map[0xE0] = {
[0x2c] = {4,3}, /* z */
[0xc7] = {5,0}, /* Home */
[0x2a] = {5,1}, /* shift */
- [0x39] = {5,2}, /* space */
+ /*
+ * There are two matrix positions which map to space,
+ * but QEMU can only use one of them for the reverse
+ * mapping, so simply use the second one.
+ */
+ /* [0x39] = {5,2}, space */
[0x39] = {5,3}, /* space */
- [0x1c] = {5,5}, /* enter */
+ /*
+ * Matrix position {5,4} and other keys are missing here.
+ * TODO: Compare with Linux code and test real hardware.
+ */
+ [0x1c] = {5,5}, /* enter (TODO: might be wrong) */
[0xc8] = {6,0}, /* up */
[0xd0] = {6,1}, /* down */
[0xcb] = {6,2}, /* left */
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index f7e8b7e8fa..aef2bde0c4 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -390,7 +390,6 @@ static void vpb_sic_class_init(ObjectClass *klass, void *data)
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = vpb_sic_init;
- dc->no_user = 1;
dc->vmsd = &vmstate_vpb_sic;
}
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index f48de00a1a..ef1707aef0 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -419,13 +419,13 @@ static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells,
int rc;
char *nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, addr);
- rc = qemu_devtree_add_subnode(fdt, nodename);
- rc |= qemu_devtree_setprop_string(fdt, nodename,
- "compatible", "virtio,mmio");
- rc |= qemu_devtree_setprop_sized_cells(fdt, nodename, "reg",
- acells, addr, scells, size);
- qemu_devtree_setprop_cells(fdt, nodename, "interrupt-parent", intc);
- qemu_devtree_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1);
+ rc = qemu_fdt_add_subnode(fdt, nodename);
+ rc |= qemu_fdt_setprop_string(fdt, nodename,
+ "compatible", "virtio,mmio");
+ rc |= qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
+ acells, addr, scells, size);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", intc);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1);
g_free(nodename);
if (rc) {
return -1;
@@ -456,8 +456,8 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
uint32_t acells, scells, intc;
const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info;
- acells = qemu_devtree_getprop_cell(fdt, "/", "#address-cells");
- scells = qemu_devtree_getprop_cell(fdt, "/", "#size-cells");
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells");
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells");
intc = find_int_controller(fdt);
if (!intc) {
/* Not fatal, we just won't provide virtio. This will
@@ -480,6 +480,36 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
}
}
+
+/* Open code a private version of pflash registration since we
+ * need to set non-default device width for VExpress platform.
+ */
+static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
+ DriveInfo *di)
+{
+ DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
+
+ if (di && qdev_prop_set_drive(dev, "drive", di->bdrv)) {
+ abort();
+ }
+
+ qdev_prop_set_uint32(dev, "num-blocks",
+ VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE);
+ 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_uint16(dev, "id0", 0x89);
+ qdev_prop_set_uint16(dev, "id1", 0x18);
+ qdev_prop_set_uint16(dev, "id2", 0x00);
+ qdev_prop_set_uint16(dev, "id3", 0x00);
+ qdev_prop_set_string(dev, "name", name);
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
+ return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
+}
+
static void vexpress_common_init(VEDBoardInfo *daughterboard,
QEMUMachineInitArgs *args)
{
@@ -561,11 +591,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
dinfo = drive_get_next(IF_PFLASH);
- pflash0 = pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
- VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
- VEXPRESS_FLASH_SECT_SIZE,
- VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
- 0x00, 0x89, 0x00, 0x18, 0);
+ pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0",
+ dinfo);
if (!pflash0) {
fprintf(stderr, "vexpress: error registering flash 0.\n");
exit(1);
@@ -580,11 +607,8 @@ static void vexpress_common_init(VEDBoardInfo *daughterboard,
}
dinfo = drive_get_next(IF_PFLASH);
- if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
- VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
- VEXPRESS_FLASH_SECT_SIZE,
- VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
- 0x00, 0x89, 0x00, 0x18, 0)) {
+ if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1",
+ dinfo)) {
fprintf(stderr, "vexpress: error registering flash 1.\n");
exit(1);
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 9531b5a574..517f2fe30f 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -156,42 +156,42 @@ static void create_fdt(VirtBoardInfo *vbi)
vbi->fdt = fdt;
/* Header */
- qemu_devtree_setprop_string(fdt, "/", "compatible", "linux,dummy-virt");
- qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 0x2);
- qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt");
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
/*
* /chosen and /memory nodes must exist for load_dtb
* to fill in necessary properties later
*/
- qemu_devtree_add_subnode(fdt, "/chosen");
- qemu_devtree_add_subnode(fdt, "/memory");
- qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory");
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ qemu_fdt_add_subnode(fdt, "/memory");
+ qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory");
/* Clock node, for the benefit of the UART. The kernel device tree
* binding documentation claims the PL011 node clock properties are
* optional but in practice if you omit them the kernel refuses to
* probe for the device.
*/
- vbi->clock_phandle = qemu_devtree_alloc_phandle(fdt);
- qemu_devtree_add_subnode(fdt, "/apb-pclk");
- qemu_devtree_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock");
- qemu_devtree_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0);
- qemu_devtree_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000);
- qemu_devtree_setprop_string(fdt, "/apb-pclk", "clock-output-names",
+ vbi->clock_phandle = qemu_fdt_alloc_phandle(fdt);
+ qemu_fdt_add_subnode(fdt, "/apb-pclk");
+ qemu_fdt_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock");
+ qemu_fdt_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000);
+ qemu_fdt_setprop_string(fdt, "/apb-pclk", "clock-output-names",
"clk24mhz");
- qemu_devtree_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle);
+ qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle);
/* No PSCI for TCG yet */
if (kvm_enabled()) {
- qemu_devtree_add_subnode(fdt, "/psci");
- qemu_devtree_setprop_string(fdt, "/psci", "compatible", "arm,psci");
- qemu_devtree_setprop_string(fdt, "/psci", "method", "hvc");
- qemu_devtree_setprop_cell(fdt, "/psci", "cpu_suspend",
+ qemu_fdt_add_subnode(fdt, "/psci");
+ qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
+ qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc");
+ qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend",
PSCI_FN_CPU_SUSPEND);
- qemu_devtree_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF);
- qemu_devtree_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON);
- qemu_devtree_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE);
+ qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", PSCI_FN_CPU_OFF);
+ qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", PSCI_FN_CPU_ON);
+ qemu_fdt_setprop_cell(fdt, "/psci", "migrate", PSCI_FN_MIGRATE);
}
}
@@ -206,10 +206,10 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
- qemu_devtree_add_subnode(vbi->fdt, "/timer");
- qemu_devtree_setprop_string(vbi->fdt, "/timer",
+ qemu_fdt_add_subnode(vbi->fdt, "/timer");
+ qemu_fdt_setprop_string(vbi->fdt, "/timer",
"compatible", "arm,armv7-timer");
- qemu_devtree_setprop_cells(vbi->fdt, "/timer", "interrupts",
+ qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts",
GIC_FDT_IRQ_TYPE_PPI, 13, irqflags,
GIC_FDT_IRQ_TYPE_PPI, 14, irqflags,
GIC_FDT_IRQ_TYPE_PPI, 11, irqflags,
@@ -220,25 +220,25 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
{
int cpu;
- qemu_devtree_add_subnode(vbi->fdt, "/cpus");
- qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1);
- qemu_devtree_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_add_subnode(vbi->fdt, "/cpus");
+ qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0);
for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) {
char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
- qemu_devtree_add_subnode(vbi->fdt, nodename);
- qemu_devtree_setprop_string(vbi->fdt, nodename, "device_type", "cpu");
- qemu_devtree_setprop_string(vbi->fdt, nodename, "compatible",
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "cpu");
+ qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
armcpu->dtb_compatible);
if (vbi->smp_cpus > 1) {
- qemu_devtree_setprop_string(vbi->fdt, nodename,
+ qemu_fdt_setprop_string(vbi->fdt, nodename,
"enable-method", "psci");
}
- qemu_devtree_setprop_cell(vbi->fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
g_free(nodename);
}
}
@@ -247,20 +247,20 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi)
{
uint32_t gic_phandle;
- gic_phandle = qemu_devtree_alloc_phandle(vbi->fdt);
- qemu_devtree_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
+ gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
- qemu_devtree_add_subnode(vbi->fdt, "/intc");
- qemu_devtree_setprop_string(vbi->fdt, "/intc", "compatible",
+ qemu_fdt_add_subnode(vbi->fdt, "/intc");
+ qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
vbi->gic_compatible);
- qemu_devtree_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
- qemu_devtree_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
- qemu_devtree_setprop_sized_cells(vbi->fdt, "/intc", "reg",
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
+ qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
2, vbi->memmap[VIRT_GIC_DIST].base,
2, vbi->memmap[VIRT_GIC_DIST].size,
2, vbi->memmap[VIRT_GIC_CPU].base,
2, vbi->memmap[VIRT_GIC_CPU].size);
- qemu_devtree_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
@@ -275,18 +275,18 @@ static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
sysbus_create_simple("pl011", base, pic[irq]);
nodename = g_strdup_printf("/pl011@%" PRIx64, base);
- qemu_devtree_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
/* Note that we can't use setprop_string because of the embedded NUL */
- qemu_devtree_setprop(vbi->fdt, nodename, "compatible",
+ qemu_fdt_setprop(vbi->fdt, nodename, "compatible",
compat, sizeof(compat));
- qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base, 2, size);
- qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts",
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq,
GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
- qemu_devtree_setprop_cells(vbi->fdt, nodename, "clocks",
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks",
vbi->clock_phandle, vbi->clock_phandle);
- qemu_devtree_setprop(vbi->fdt, nodename, "clock-names",
+ qemu_fdt_setprop(vbi->fdt, nodename, "clock-names",
clocknames, sizeof(clocknames));
g_free(nodename);
}
@@ -314,14 +314,14 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size;
nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
- qemu_devtree_add_subnode(vbi->fdt, nodename);
- qemu_devtree_setprop_string(vbi->fdt, nodename,
- "compatible", "virtio,mmio");
- qemu_devtree_setprop_sized_cells(vbi->fdt, nodename, "reg",
- 2, base, 2, size);
- qemu_devtree_setprop_cells(vbi->fdt, nodename, "interrupts",
- GIC_FDT_IRQ_TYPE_SPI, irq,
- GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
+ qemu_fdt_add_subnode(vbi->fdt, nodename);
+ qemu_fdt_setprop_string(vbi->fdt, nodename,
+ "compatible", "virtio,mmio");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
+ 2, base, 2, size);
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
+ GIC_FDT_IRQ_TYPE_SPI, irq,
+ GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
g_free(nodename);
}
}
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 46924a0391..98e0958a77 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -25,6 +25,7 @@
#include "sysemu/blockdev.h"
#include "hw/loader.h"
#include "hw/ssi.h"
+#include "qemu/error-report.h"
#define NUM_SPI_FLASHES 4
#define NUM_QSPI_FLASHES 2
@@ -35,6 +36,8 @@
#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */
+#define MPCORE_PERIPHBASE 0xF8F00000
+
static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
@@ -46,9 +49,11 @@ static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
DeviceState *dev;
SysBusDevice *s;
- qemu_check_nic_model(nd, "cadence_gem");
dev = qdev_create(NULL, "cadence_gem");
- qdev_set_nic_properties(dev, nd);
+ if (nd->used) {
+ qemu_check_nic_model(nd, "cadence_gem");
+ qdev_set_nic_properties(dev, nd);
+ }
qdev_init_nofail(dev);
s = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(s, 0, base);
@@ -102,6 +107,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
const char *kernel_filename = args->kernel_filename;
const char *kernel_cmdline = args->kernel_cmdline;
const char *initrd_filename = args->initrd_filename;
+ ObjectClass *cpu_oc;
ARMCPU *cpu;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
@@ -109,16 +115,24 @@ static void zynq_init(QEMUMachineInitArgs *args)
DeviceState *dev;
SysBusDevice *busdev;
qemu_irq pic[64];
- NICInfo *nd;
+ Error *err = NULL;
int n;
if (!cpu_model) {
cpu_model = "cortex-a9";
}
+ cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model);
- cpu = cpu_arm_init(cpu_model);
- if (!cpu) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc)));
+
+ object_property_set_int(OBJECT(cpu), MPCORE_PERIPHBASE, "reset-cbar", &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+ object_property_set_bool(OBJECT(cpu), true, "realized", &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
exit(1);
}
@@ -154,7 +168,7 @@ static void zynq_init(QEMUMachineInitArgs *args)
qdev_prop_set_uint32(dev, "num-cpu", 1);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
- sysbus_mmio_map(busdev, 0, 0xF8F00000);
+ sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
sysbus_connect_irq(busdev, 0,
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
@@ -177,14 +191,8 @@ static void zynq_init(QEMUMachineInitArgs *args)
sysbus_create_varargs("cadence_ttc", 0xF8002000,
pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL);
- for (n = 0; n < nb_nics; n++) {
- nd = &nd_table[n];
- if (n == 0) {
- gem_init(nd, 0xE000B000, pic[54-IRQ_OFFSET]);
- } else if (n == 1) {
- gem_init(nd, 0xE000C000, pic[77-IRQ_OFFSET]);
- }
- }
+ gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]);
+ gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]);
dev = qdev_create(NULL, "generic-sdhci");
qdev_init_nofail(dev);
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index d52c5019b3..97367b1f8b 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -33,7 +33,7 @@
#define DPRINTF(fmt, ...)
#endif
-static struct keymap map[0x100] = {
+static const struct keymap map[0x100] = {
[0 ... 0xff] = { -1, -1 },
[0x3b] = {0, 0}, /* Option = F1 */
[0xc8] = {0, 1}, /* Up */
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 07a43bfe89..986f2a9c92 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -559,6 +559,21 @@ static int hda_audio_post_load(void *opaque, int version)
return 0;
}
+static void hda_audio_reset(DeviceState *dev)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda.qdev, dev);
+ HDAAudioStream *st;
+ int i;
+
+ dprint(a, 1, "%s\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+ st = a->st + i;
+ if (st->node != NULL) {
+ hda_audio_set_running(st, false);
+ }
+ }
+}
+
static const VMStateDescription vmstate_hda_audio_stream = {
.name = "hda-audio-stream",
.version_id = 1,
@@ -640,6 +655,7 @@ static void hda_audio_output_class_init(ObjectClass *klass, void *data)
k->stream = hda_audio_stream;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "HDA Audio Codec, output-only (line-out)";
+ dc->reset = hda_audio_reset;
dc->vmsd = &vmstate_hda_audio;
dc->props = hda_audio_properties;
}
@@ -662,6 +678,7 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
k->stream = hda_audio_stream;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
+ dc->reset = hda_audio_reset;
dc->vmsd = &vmstate_hda_audio;
dc->props = hda_audio_properties;
}
@@ -684,6 +701,7 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
k->stream = hda_audio_stream;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
+ dc->reset = hda_audio_reset;
dc->vmsd = &vmstate_hda_audio;
dc->props = hda_audio_properties;
}
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 6ab8c245d3..d41f82cec4 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -900,7 +900,7 @@ static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, hwaddr addr)
{
const IntelHDAReg *reg;
- if (addr >= sizeof(regtab)/sizeof(regtab[0])) {
+ if (addr >= ARRAY_SIZE(regtab)) {
goto noreg;
}
reg = regtab+addr;
@@ -1025,7 +1025,7 @@ static void intel_hda_regs_reset(IntelHDAState *d)
uint32_t *addr;
int i;
- for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(regtab); i++) {
if (regtab[i].name == NULL) {
continue;
}
diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c
index 97194ce7ad..cdce238f55 100644
--- a/hw/audio/marvell_88w8618.c
+++ b/hw/audio/marvell_88w8618.c
@@ -288,6 +288,8 @@ static void mv88w8618_audio_class_init(ObjectClass *klass, void *data)
dc->reset = mv88w8618_audio_reset;
dc->vmsd = &mv88w8618_audio_vmsd;
dc->props = mv88w8618_audio_properties;
+ /* Reason: pointer property "wm8750" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo mv88w8618_audio_info = {
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index 9004ce3d1f..f980d66b2f 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -192,8 +192,9 @@ static void pcspk_class_initfn(ObjectClass *klass, void *data)
dc->realize = pcspk_realizefn;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
- dc->no_user = 1;
dc->props = pcspk_properties;
+ /* Reason: pointer property "pit", realize sets global pcspk_state */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo pcspk_info = {
diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c
index 5393b520b7..ed82be54e8 100644
--- a/hw/audio/pl041.c
+++ b/hw/audio/pl041.c
@@ -632,7 +632,6 @@ static void pl041_device_class_init(ObjectClass *klass, void *data)
k->init = pl041_init;
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
- dc->no_user = 1;
dc->reset = pl041_device_reset;
dc->vmsd = &vmstate_pl041;
dc->props = pl041_device_properties;
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index f2d7350a50..456d437ac3 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -35,7 +35,7 @@ enum {
typedef struct {
struct iocb iocb; /* Linux AIO control block */
QEMUIOVector *inhdr; /* iovecs for virtio_blk_inhdr */
- unsigned int head; /* vring descriptor index */
+ VirtQueueElement *elem; /* saved data from the virtqueue */
struct iovec *bounce_iov; /* used if guest buffers are unaligned */
QEMUIOVector *read_qiov; /* for read completion /w bounce buffer */
} VirtIOBlockRequest;
@@ -96,7 +96,7 @@ static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque)
len = 0;
}
- trace_virtio_blk_data_plane_complete_request(s, req->head, ret);
+ trace_virtio_blk_data_plane_complete_request(s, req->elem->index, ret);
if (req->read_qiov) {
assert(req->bounce_iov);
@@ -118,12 +118,12 @@ static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque)
* written to, but for virtio-blk it seems to be the number of bytes
* transferred plus the status bytes.
*/
- vring_push(&s->vring, req->head, len + sizeof(hdr));
-
+ vring_push(&s->vring, req->elem, len + sizeof(hdr));
+ req->elem = NULL;
s->num_reqs--;
}
-static void complete_request_early(VirtIOBlockDataPlane *s, unsigned int head,
+static void complete_request_early(VirtIOBlockDataPlane *s, VirtQueueElement *elem,
QEMUIOVector *inhdr, unsigned char status)
{
struct virtio_blk_inhdr hdr = {
@@ -134,26 +134,26 @@ static void complete_request_early(VirtIOBlockDataPlane *s, unsigned int head,
qemu_iovec_destroy(inhdr);
g_slice_free(QEMUIOVector, inhdr);
- vring_push(&s->vring, head, sizeof(hdr));
+ vring_push(&s->vring, elem, sizeof(hdr));
notify_guest(s);
}
/* Get disk serial number */
static void do_get_id_cmd(VirtIOBlockDataPlane *s,
struct iovec *iov, unsigned int iov_cnt,
- unsigned int head, QEMUIOVector *inhdr)
+ VirtQueueElement *elem, QEMUIOVector *inhdr)
{
char id[VIRTIO_BLK_ID_BYTES];
/* Serial number not NUL-terminated when shorter than buffer */
strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id));
iov_from_buf(iov, iov_cnt, 0, id, sizeof(id));
- complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK);
+ complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
}
static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
- struct iovec *iov, unsigned int iov_cnt,
- long long offset, unsigned int head,
+ struct iovec *iov, unsigned iov_cnt,
+ long long offset, VirtQueueElement *elem,
QEMUIOVector *inhdr)
{
struct iocb *iocb;
@@ -186,19 +186,20 @@ static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read,
/* Fill in virtio block metadata needed for completion */
VirtIOBlockRequest *req = container_of(iocb, VirtIOBlockRequest, iocb);
- req->head = head;
+ req->elem = elem;
req->inhdr = inhdr;
req->bounce_iov = bounce_iov;
req->read_qiov = read_qiov;
return 0;
}
-static int process_request(IOQueue *ioq, struct iovec iov[],
- unsigned int out_num, unsigned int in_num,
- unsigned int head)
+static int process_request(IOQueue *ioq, VirtQueueElement *elem)
{
VirtIOBlockDataPlane *s = container_of(ioq, VirtIOBlockDataPlane, ioqueue);
- struct iovec *in_iov = &iov[out_num];
+ struct iovec *iov = elem->out_sg;
+ struct iovec *in_iov = elem->in_sg;
+ unsigned out_num = elem->out_num;
+ unsigned in_num = elem->in_num;
struct virtio_blk_outhdr outhdr;
QEMUIOVector *inhdr;
size_t in_size;
@@ -229,29 +230,29 @@ static int process_request(IOQueue *ioq, struct iovec iov[],
switch (outhdr.type) {
case VIRTIO_BLK_T_IN:
- do_rdwr_cmd(s, true, in_iov, in_num, outhdr.sector * 512, head, inhdr);
+ do_rdwr_cmd(s, true, in_iov, in_num, outhdr.sector * 512, elem, inhdr);
return 0;
case VIRTIO_BLK_T_OUT:
- do_rdwr_cmd(s, false, iov, out_num, outhdr.sector * 512, head, inhdr);
+ do_rdwr_cmd(s, false, iov, out_num, outhdr.sector * 512, elem, inhdr);
return 0;
case VIRTIO_BLK_T_SCSI_CMD:
/* TODO support SCSI commands */
- complete_request_early(s, head, inhdr, VIRTIO_BLK_S_UNSUPP);
+ complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_UNSUPP);
return 0;
case VIRTIO_BLK_T_FLUSH:
/* TODO fdsync not supported by Linux AIO, do it synchronously here! */
if (qemu_fdatasync(s->fd) < 0) {
- complete_request_early(s, head, inhdr, VIRTIO_BLK_S_IOERR);
+ complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_IOERR);
} else {
- complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK);
+ complete_request_early(s, elem, inhdr, VIRTIO_BLK_S_OK);
}
return 0;
case VIRTIO_BLK_T_GET_ID:
- do_get_id_cmd(s, in_iov, in_num, head, inhdr);
+ do_get_id_cmd(s, in_iov, in_num, elem, inhdr);
return 0;
default:
@@ -267,29 +268,8 @@ static void handle_notify(EventNotifier *e)
VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
host_notifier);
- /* There is one array of iovecs into which all new requests are extracted
- * from the vring. Requests are read from the vring and the translated
- * descriptors are written to the iovecs array. The iovecs do not have to
- * persist across handle_notify() calls because the kernel copies the
- * iovecs on io_submit().
- *
- * Handling io_submit() EAGAIN may require storing the requests across
- * handle_notify() calls until the kernel has sufficient resources to
- * accept more I/O. This is not implemented yet.
- */
- struct iovec iovec[VRING_MAX];
- struct iovec *end = &iovec[VRING_MAX];
- struct iovec *iov = iovec;
-
- /* When a request is read from the vring, the index of the first descriptor
- * (aka head) is returned so that the completed request can be pushed onto
- * the vring later.
- *
- * The number of hypervisor read-only iovecs is out_num. The number of
- * hypervisor write-only iovecs is in_num.
- */
- int head;
- unsigned int out_num = 0, in_num = 0;
+ VirtQueueElement *elem;
+ int ret;
unsigned int num_queued;
event_notifier_test_and_clear(&s->host_notifier);
@@ -298,29 +278,31 @@ static void handle_notify(EventNotifier *e)
vring_disable_notification(s->vdev, &s->vring);
for (;;) {
- head = vring_pop(s->vdev, &s->vring, iov, end, &out_num, &in_num);
- if (head < 0) {
+ ret = vring_pop(s->vdev, &s->vring, &elem);
+ if (ret < 0) {
+ assert(elem == NULL);
break; /* no more requests */
}
- trace_virtio_blk_data_plane_process_request(s, out_num, in_num,
- head);
+ trace_virtio_blk_data_plane_process_request(s, elem->out_num,
+ elem->in_num, elem->index);
- if (process_request(&s->ioqueue, iov, out_num, in_num, head) < 0) {
+ if (process_request(&s->ioqueue, elem) < 0) {
vring_set_broken(&s->vring);
+ vring_free_element(elem);
+ ret = -EFAULT;
break;
}
- iov += out_num + in_num;
}
- if (likely(head == -EAGAIN)) { /* vring emptied */
+ if (likely(ret == -EAGAIN)) { /* vring emptied */
/* Re-enable guest->host notifies and stop processing the vring.
* But if the guest has snuck in more descriptors, keep processing.
*/
if (vring_enable_notification(s->vdev, &s->vring)) {
break;
}
- } else { /* head == -ENOBUFS or fatal error, iovecs[] is depleted */
+ } else { /* ret == -ENOBUFS or fatal error, iovecs[] is depleted */
/* Since there are no iovecs[] left, stop processing for now. Do
* not re-enable guest->host notifies since the I/O completion
* handler knows to check for more vring descriptors anyway.
@@ -380,8 +362,9 @@ static void start_data_plane_bh(void *opaque)
s, QEMU_THREAD_JOINABLE);
}
-bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
- VirtIOBlockDataPlane **dataplane)
+void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
+ VirtIOBlockDataPlane **dataplane,
+ Error **errp)
{
VirtIOBlockDataPlane *s;
int fd;
@@ -389,33 +372,35 @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
*dataplane = NULL;
if (!blk->data_plane) {
- return true;
+ return;
}
if (blk->scsi) {
- error_report("device is incompatible with x-data-plane, use scsi=off");
- return false;
+ error_setg(errp,
+ "device is incompatible with x-data-plane, use scsi=off");
+ return;
}
if (blk->config_wce) {
- error_report("device is incompatible with x-data-plane, "
- "use config-wce=off");
- return false;
+ error_setg(errp, "device is incompatible with x-data-plane, "
+ "use config-wce=off");
+ return;
}
/* If dataplane is (re-)enabled while the guest is running there could be
* block jobs that can conflict.
*/
if (bdrv_in_use(blk->conf.bs)) {
- error_report("cannot start dataplane thread while device is in use");
- return false;
+ error_setg(errp,
+ "cannot start dataplane thread while device is in use");
+ return;
}
fd = raw_get_aio_fd(blk->conf.bs);
if (fd < 0) {
- error_report("drive is incompatible with x-data-plane, "
- "use format=raw,cache=none,aio=native");
- return false;
+ error_setg(errp, "drive is incompatible with x-data-plane, "
+ "use format=raw,cache=none,aio=native");
+ return;
}
s = g_new0(VirtIOBlockDataPlane, 1);
@@ -427,7 +412,6 @@ bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
bdrv_set_in_use(blk->conf.bs, 1);
*dataplane = s;
- return true;
}
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h
index c90e99f48f..1750c9905b 100644
--- a/hw/block/dataplane/virtio-blk.h
+++ b/hw/block/dataplane/virtio-blk.h
@@ -19,8 +19,9 @@
typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane;
-bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
- VirtIOBlockDataPlane **dataplane);
+void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
+ VirtIOBlockDataPlane **dataplane,
+ Error **errp);
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s);
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s);
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index c5a6c21215..592b58f9b5 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -2234,7 +2234,6 @@ static void isabus_fdc_class_init(ObjectClass *klass, void *data)
dc->realize = isabus_fdc_realize;
dc->fw_name = "fdc";
- dc->no_user = 1;
dc->reset = fdctrl_external_reset_isa;
dc->vmsd = &vmstate_isa_fdc;
dc->props = isa_fdc_properties;
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 018a9677ba..0c95d53dca 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -40,6 +40,7 @@
#include "hw/block/flash.h"
#include "block/block.h"
#include "qemu/timer.h"
+#include "qemu/bitops.h"
#include "exec/address-spaces.h"
#include "qemu/host-utils.h"
#include "hw/sysbus.h"
@@ -71,7 +72,9 @@ struct pflash_t {
BlockDriverState *bs;
uint32_t nb_blocs;
uint64_t sector_len;
- uint8_t width;
+ 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;
uint8_t wcycle; /* if 0, the flash is read normally */
int ro;
@@ -116,6 +119,119 @@ static void pflash_timer (void *opaque)
pfl->cmd = 0;
}
+/* Perform a CFI query based on the bank width of the flash.
+ * If this code is called we know we have a device_width set for
+ * this flash.
+ */
+static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset)
+{
+ int i;
+ uint32_t resp = 0;
+ hwaddr boff;
+
+ /* Adjust incoming offset to match expected device-width
+ * addressing. CFI query addresses are always specified in terms of
+ * the maximum supported width of the device. This means that x8
+ * devices and x8/x16 devices in x8 mode behave differently. For
+ * devices that are not used at their max width, we will be
+ * provided with addresses that use higher address bits than
+ * expected (based on the max width), so we will shift them lower
+ * so that they will match the addresses used when
+ * device_width==max_device_width.
+ */
+ boff = offset >> (ctz32(pfl->bank_width) +
+ ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
+
+ if (boff > pfl->cfi_len) {
+ return 0;
+ }
+ /* Now we will construct the CFI response generated by a single
+ * device, then replicate that for all devices that make up the
+ * bus. For wide parts used in x8 mode, CFI query responses
+ * are different than native byte-wide parts.
+ */
+ resp = pfl->cfi_table[boff];
+ if (pfl->device_width != pfl->max_device_width) {
+ /* The only case currently supported is x8 mode for a
+ * wider part.
+ */
+ if (pfl->device_width != 1 || pfl->bank_width > 4) {
+ DPRINTF("%s: Unsupported device configuration: "
+ "device_width=%d, max_device_width=%d\n",
+ __func__, pfl->device_width,
+ pfl->max_device_width);
+ return 0;
+ }
+ /* CFI query data is repeated, rather than zero padded for
+ * wide devices used in x8 mode.
+ */
+ for (i = 1; i < pfl->max_device_width; i++) {
+ resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]);
+ }
+ }
+ /* Replicate responses for each device in bank. */
+ if (pfl->device_width < pfl->bank_width) {
+ for (i = pfl->device_width;
+ i < pfl->bank_width; i += pfl->device_width) {
+ resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
+ }
+ }
+
+ return resp;
+}
+
+
+
+/* Perform a device id query based on the bank width of the flash. */
+static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
+{
+ int i;
+ uint32_t resp;
+ hwaddr boff;
+
+ /* Adjust incoming offset to match expected device-width
+ * addressing. Device ID read addresses are always specified in
+ * terms of the maximum supported width of the device. This means
+ * that x8 devices and x8/x16 devices in x8 mode behave
+ * differently. For devices that are not used at their max width,
+ * we will be provided with addresses that use higher address bits
+ * than expected (based on the max width), so we will shift them
+ * lower so that they will match the addresses used when
+ * device_width==max_device_width.
+ */
+ boff = offset >> (ctz32(pfl->bank_width) +
+ ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
+
+ /* Mask off upper bits which may be used in to query block
+ * or sector lock status at other addresses.
+ * Offsets 2/3 are block lock status, is not emulated.
+ */
+ switch (boff & 0xFF) {
+ case 0:
+ resp = pfl->ident0;
+ DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
+ break;
+ case 1:
+ resp = pfl->ident1;
+ DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
+ break;
+ default:
+ DPRINTF("%s: Read Device Information offset=%x\n", __func__,
+ (unsigned)offset);
+ return 0;
+ break;
+ }
+ /* Replicate responses for each device in bank. */
+ if (pfl->device_width < pfl->bank_width) {
+ for (i = pfl->device_width;
+ i < pfl->bank_width; i += pfl->device_width) {
+ resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp);
+ }
+ }
+
+ return resp;
+}
+
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
int width, int be)
{
@@ -124,12 +240,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
uint8_t *p;
ret = -1;
- boff = offset & 0xFF; /* why this here ?? */
-
- if (pfl->width == 2)
- boff = boff >> 1;
- else if (pfl->width == 4)
- boff = boff >> 2;
#if 0
DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
@@ -190,35 +300,88 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
case 0x60: /* Block /un)lock */
case 0x70: /* Status Register */
case 0xe8: /* Write block */
- /* Status register read */
+ /* Status register read. Return status from each device in
+ * bank.
+ */
ret = pfl->status;
- if (width > 2) {
+ if (pfl->device_width && width > pfl->device_width) {
+ int shift = pfl->device_width * 8;
+ while (shift + pfl->device_width * 8 <= width * 8) {
+ ret |= pfl->status << shift;
+ shift += pfl->device_width * 8;
+ }
+ } else if (!pfl->device_width && width > 2) {
+ /* Handle 32 bit flash cases where device width is not
+ * set. (Existing behavior before device width added.)
+ */
ret |= pfl->status << 16;
}
DPRINTF("%s: status %x\n", __func__, ret);
break;
case 0x90:
- switch (boff) {
- case 0:
- ret = pfl->ident0 << 8 | pfl->ident1;
- DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
- break;
- case 1:
- ret = pfl->ident2 << 8 | pfl->ident3;
- DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
- break;
- default:
- DPRINTF("%s: Read Device Information boff=%x\n", __func__,
- (unsigned)boff);
- ret = 0;
- break;
+ if (!pfl->device_width) {
+ /* Preserve old behavior if device width not specified */
+ boff = offset & 0xFF;
+ if (pfl->bank_width == 2) {
+ boff = boff >> 1;
+ } else if (pfl->bank_width == 4) {
+ boff = boff >> 2;
+ }
+
+ switch (boff) {
+ case 0:
+ ret = pfl->ident0 << 8 | pfl->ident1;
+ DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
+ break;
+ case 1:
+ ret = pfl->ident2 << 8 | pfl->ident3;
+ DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
+ break;
+ default:
+ DPRINTF("%s: Read Device Information boff=%x\n", __func__,
+ (unsigned)boff);
+ ret = 0;
+ break;
+ }
+ } else {
+ /* If we have a read larger than the bank_width, combine multiple
+ * manufacturer/device ID queries into a single response.
+ */
+ int i;
+ for (i = 0; i < width; i += pfl->bank_width) {
+ ret = deposit32(ret, i * 8, pfl->bank_width * 8,
+ pflash_devid_query(pfl,
+ offset + i * pfl->bank_width));
+ }
}
break;
case 0x98: /* Query mode */
- if (boff > pfl->cfi_len)
- ret = 0;
- else
- ret = pfl->cfi_table[boff];
+ if (!pfl->device_width) {
+ /* Preserve old behavior if device width not specified */
+ boff = offset & 0xFF;
+ if (pfl->bank_width == 2) {
+ boff = boff >> 1;
+ } else if (pfl->bank_width == 4) {
+ boff = boff >> 2;
+ }
+
+ if (boff > pfl->cfi_len) {
+ ret = 0;
+ } else {
+ ret = pfl->cfi_table[boff];
+ }
+ } else {
+ /* If we have a read larger than the bank_width, combine multiple
+ * CFI queries into a single response.
+ */
+ int i;
+ for (i = 0; i < width; i += pfl->bank_width) {
+ ret = deposit32(ret, i * 8, pfl->bank_width * 8,
+ pflash_cfi_query(pfl,
+ offset + i * pfl->bank_width));
+ }
+ }
+
break;
}
return ret;
@@ -378,6 +541,14 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
break;
case 0xe8:
+ /* Mask writeblock size based on device width, or bank width if
+ * device width not specified.
+ */
+ if (pfl->device_width) {
+ value = extract32(value, 0, pfl->device_width * 8);
+ } else {
+ value = extract32(value, 0, pfl->bank_width * 8);
+ }
DPRINTF("%s: block write of %x bytes\n", __func__, value);
pfl->counter = value;
pfl->wcycle++;
@@ -613,6 +784,13 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pfl->ro = 0;
}
+ /* Default to devices being used at their maximum device width. This was
+ * assumed before the device_width support was added.
+ */
+ if (!pfl->max_device_width) {
+ pfl->max_device_width = pfl->device_width;
+ }
+
pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl);
pfl->wcycle = 0;
pfl->cmd = 0;
@@ -665,7 +843,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
pfl->cfi_table[0x28] = 0x02;
pfl->cfi_table[0x29] = 0x00;
/* Max number of bytes in multi-bytes write */
- if (pfl->width == 1) {
+ if (pfl->bank_width == 1) {
pfl->cfi_table[0x2A] = 0x08;
} else {
pfl->cfi_table[0x2A] = 0x0B;
@@ -706,7 +884,25 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
- DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
+ /* width here is the overall width of this QEMU device in bytes.
+ * The QEMU device may be emulating a number of flash devices
+ * wired up in parallel; the width of each individual flash
+ * device should be specified via device-width. If the individual
+ * devices have a maximum width which is greater than the width
+ * they are being used for, this maximum width should be set via
+ * max-device-width (which otherwise defaults to device-width).
+ * So for instance a 32-bit wide QEMU flash device made from four
+ * 16-bit flash devices used in 8-bit wide mode would be configured
+ * with width = 4, device-width = 1, max-device-width = 2.
+ *
+ * If device-width is not specified we default to backwards
+ * compatible behaviour which is a bad emulation of two
+ * 16 bit devices making up a 32 bit wide QEMU device. This
+ * is deprecated for new uses of this device.
+ */
+ 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_UINT16("id0", struct pflash_t, ident0, 0),
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
@@ -745,8 +941,8 @@ pflash_t *pflash_cfi01_register(hwaddr base,
DeviceState *qdev, const char *name,
hwaddr size,
BlockDriverState *bs,
- uint32_t sector_len, int nb_blocs, int width,
- uint16_t id0, uint16_t id1,
+ uint32_t sector_len, int nb_blocs,
+ int bank_width, uint16_t id0, uint16_t id1,
uint16_t id2, uint16_t id3, int be)
{
DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01);
@@ -756,7 +952,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", width);
+ qdev_prop_set_uint8(dev, "width", bank_width);
qdev_prop_set_uint8(dev, "big-endian", !!be);
qdev_prop_set_uint16(dev, "id0", id0);
qdev_prop_set_uint16(dev, "id1", id1);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 13f6d8276e..8a568e5edb 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -657,6 +657,7 @@ static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
VirtIOBlock *s = container_of(notifier, VirtIOBlock,
migration_state_notifier);
MigrationState *mig = data;
+ Error *err = NULL;
if (migration_in_setup(mig)) {
if (!s->dataplane) {
@@ -671,31 +672,39 @@ static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
}
bdrv_drain_all(); /* complete in-flight non-dataplane requests */
virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->blk,
- &s->dataplane);
+ &s->dataplane, &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
+ error_free(err);
+ }
}
}
#endif /* CONFIG_VIRTIO_BLK_DATA_PLANE */
-static int virtio_blk_device_init(VirtIODevice *vdev)
+static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
{
- DeviceState *qdev = DEVICE(vdev);
- VirtIOBlock *s = VIRTIO_BLK(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOBlock *s = VIRTIO_BLK(dev);
VirtIOBlkConf *blk = &(s->blk);
+#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
+ Error *err = NULL;
+#endif
static int virtio_blk_id;
if (!blk->conf.bs) {
- error_report("drive property not set");
- return -1;
+ error_setg(errp, "drive property not set");
+ return;
}
if (!bdrv_is_inserted(blk->conf.bs)) {
- error_report("Device needs media, but drive is empty");
- return -1;
+ error_setg(errp, "Device needs media, but drive is empty");
+ return;
}
blkconf_serial(&blk->conf, &blk->serial);
s->original_wce = bdrv_enable_write_cache(blk->conf.bs);
if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) {
- return -1;
+ error_setg(errp, "Error setting geometry");
+ return;
}
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
@@ -708,30 +717,32 @@ static int virtio_blk_device_init(VirtIODevice *vdev)
s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
- if (!virtio_blk_data_plane_create(vdev, blk, &s->dataplane)) {
+ virtio_blk_data_plane_create(vdev, blk, &s->dataplane, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
virtio_cleanup(vdev);
- return -1;
+ return;
}
s->migration_state_notifier.notify = virtio_blk_migration_state_changed;
add_migration_state_change_notifier(&s->migration_state_notifier);
#endif
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
- register_savevm(qdev, "virtio-blk", virtio_blk_id++, 2,
+ register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
virtio_blk_save, virtio_blk_load, s);
bdrv_set_dev_ops(s->bs, &virtio_block_ops, s);
- bdrv_set_buffer_alignment(s->bs, s->conf->logical_block_size);
+ bdrv_set_guest_block_size(s->bs, s->conf->logical_block_size);
bdrv_iostatus_enable(s->bs);
- add_boot_device_path(s->conf->bootindex, qdev, "/disk@0,0");
- return 0;
+ add_boot_device_path(s->conf->bootindex, dev, "/disk@0,0");
}
-static int virtio_blk_device_exit(DeviceState *dev)
+static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOBlock *s = VIRTIO_BLK(dev);
+
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
remove_migration_state_change_notifier(&s->migration_state_notifier);
virtio_blk_data_plane_destroy(s->dataplane);
@@ -741,7 +752,6 @@ static int virtio_blk_device_exit(DeviceState *dev)
unregister_savevm(dev, "virtio-blk", s);
blockdev_mark_auto_del(s->bs);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_blk_properties[] = {
@@ -753,10 +763,11 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_blk_device_exit;
+
dc->props = virtio_blk_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- vdc->init = virtio_blk_device_init;
+ vdc->realize = virtio_blk_device_realize;
+ vdc->unrealize = virtio_blk_device_unrealize;
vdc->get_config = virtio_blk_update_config;
vdc->set_config = virtio_blk_set_config;
vdc->get_features = virtio_blk_get_features;
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index cbd6a006f4..be2a7d953a 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o
obj-$(CONFIG_OMAP) += omap_uart.o
obj-$(CONFIG_SH4) += sh_serial.o
obj-$(CONFIG_PSERIES) += spapr_vty.o
+obj-$(CONFIG_DIGIC) += digic-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index f18db53bca..1012f1ad64 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -34,6 +34,9 @@
#define UART_SR_INTR_RFUL 0x00000004
#define UART_SR_INTR_TEMPTY 0x00000008
#define UART_SR_INTR_TFUL 0x00000010
+/* somewhat awkwardly, TTRIG is misaligned between SR and ISR */
+#define UART_SR_TTRIG 0x00002000
+#define UART_INTR_TTRIG 0x00000400
/* bits fields in CSR that correlate to CISR. If any of these bits are set in
* SR, then the same bit in CISR is set high too */
#define UART_SR_TO_CISR_MASK 0x0000001F
@@ -43,6 +46,7 @@
#define UART_INTR_PARE 0x00000080
#define UART_INTR_TIMEOUT 0x00000100
#define UART_INTR_DMSI 0x00000200
+#define UART_INTR_TOVR 0x00001000
#define UART_SR_RACTIVE 0x00000400
#define UART_SR_TACTIVE 0x00000800
@@ -110,23 +114,37 @@
#define CADENCE_UART(obj) OBJECT_CHECK(UartState, (obj), TYPE_CADENCE_UART)
typedef struct {
+ /*< private >*/
SysBusDevice parent_obj;
+ /*< public >*/
MemoryRegion iomem;
uint32_t r[R_MAX];
- uint8_t r_fifo[RX_FIFO_SIZE];
+ uint8_t rx_fifo[RX_FIFO_SIZE];
+ uint8_t tx_fifo[TX_FIFO_SIZE];
uint32_t rx_wpos;
uint32_t rx_count;
+ uint32_t tx_count;
uint64_t char_tx_time;
CharDriverState *chr;
qemu_irq irq;
QEMUTimer *fifo_trigger_handle;
- QEMUTimer *tx_time_handle;
} UartState;
static void uart_update_status(UartState *s)
{
+ s->r[R_SR] = 0;
+
+ s->r[R_SR] |= s->rx_count == RX_FIFO_SIZE ? UART_SR_INTR_RFUL : 0;
+ s->r[R_SR] |= !s->rx_count ? UART_SR_INTR_REMPTY : 0;
+ s->r[R_SR] |= s->rx_count >= s->r[R_RTRIG] ? UART_SR_INTR_RTRIG : 0;
+
+ s->r[R_SR] |= s->tx_count == TX_FIFO_SIZE ? UART_SR_INTR_TFUL : 0;
+ s->r[R_SR] |= !s->tx_count ? UART_SR_INTR_TEMPTY : 0;
+ s->r[R_SR] |= s->tx_count >= s->r[R_TTRIG] ? UART_SR_TTRIG : 0;
+
s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK;
+ s->r[R_CISR] |= s->r[R_SR] & UART_SR_TTRIG ? UART_INTR_TTRIG : 0;
qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR]));
}
@@ -139,24 +157,6 @@ static void fifo_trigger_update(void *opaque)
uart_update_status(s);
}
-static void uart_tx_redo(UartState *s)
-{
- uint64_t new_tx_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
- timer_mod(s->tx_time_handle, new_tx_time + s->char_tx_time);
-
- s->r[R_SR] |= UART_SR_INTR_TEMPTY;
-
- uart_update_status(s);
-}
-
-static void uart_tx_write(void *opaque)
-{
- UartState *s = (UartState *)opaque;
-
- uart_tx_redo(s);
-}
-
static void uart_rx_reset(UartState *s)
{
s->rx_wpos = 0;
@@ -164,15 +164,11 @@ static void uart_rx_reset(UartState *s)
if (s->chr) {
qemu_chr_accept_input(s->chr);
}
-
- s->r[R_SR] |= UART_SR_INTR_REMPTY;
- s->r[R_SR] &= ~UART_SR_INTR_RFUL;
}
static void uart_tx_reset(UartState *s)
{
- s->r[R_SR] |= UART_SR_INTR_TEMPTY;
- s->r[R_SR] &= ~UART_SR_INTR_TFUL;
+ s->tx_count = 0;
}
static void uart_send_breaks(UartState *s)
@@ -237,8 +233,16 @@ static void uart_parameters_setup(UartState *s)
static int uart_can_receive(void *opaque)
{
UartState *s = (UartState *)opaque;
+ int ret = MAX(RX_FIFO_SIZE, TX_FIFO_SIZE);
+ uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
- return RX_FIFO_SIZE - s->rx_count;
+ if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
+ ret = MIN(ret, RX_FIFO_SIZE - s->rx_count);
+ }
+ if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
+ ret = MIN(ret, TX_FIFO_SIZE - s->tx_count);
+ }
+ return ret;
}
static void uart_ctrl_update(UartState *s)
@@ -253,10 +257,6 @@ static void uart_ctrl_update(UartState *s)
s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST);
- if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
- uart_tx_redo(s);
- }
-
if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
uart_send_breaks(s);
}
@@ -272,24 +272,13 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
return;
}
- s->r[R_SR] &= ~UART_SR_INTR_REMPTY;
-
if (s->rx_count == RX_FIFO_SIZE) {
s->r[R_CISR] |= UART_INTR_ROVR;
} else {
for (i = 0; i < size; i++) {
- s->r_fifo[s->rx_wpos] = buf[i];
+ s->rx_fifo[s->rx_wpos] = buf[i];
s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
s->rx_count++;
-
- if (s->rx_count == RX_FIFO_SIZE) {
- s->r[R_SR] |= UART_SR_INTR_RFUL;
- break;
- }
-
- if (s->rx_count >= s->r[R_RTRIG]) {
- s->r[R_SR] |= UART_SR_INTR_RTRIG;
- }
}
timer_mod(s->fifo_trigger_handle, new_rx_time +
(s->char_tx_time * 4));
@@ -297,13 +286,55 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
uart_update_status(s);
}
+static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
+ void *opaque)
+{
+ UartState *s = opaque;
+ int ret;
+
+ /* instant drain the fifo when there's no back-end */
+ if (!s->chr) {
+ s->tx_count = 0;
+ }
+
+ if (!s->tx_count) {
+ return FALSE;
+ }
+
+ ret = qemu_chr_fe_write(s->chr, s->tx_fifo, s->tx_count);
+ s->tx_count -= ret;
+ memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_count);
+
+ if (s->tx_count) {
+ int r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT, cadence_uart_xmit, s);
+ assert(r);
+ }
+
+ uart_update_status(s);
+ return FALSE;
+}
+
static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
{
if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
return;
}
- qemu_chr_fe_write_all(s->chr, buf, size);
+ if (size > TX_FIFO_SIZE - s->tx_count) {
+ size = TX_FIFO_SIZE - s->tx_count;
+ /*
+ * This can only be a guest error via a bad tx fifo register push,
+ * as can_receive() should stop remote loop and echo modes ever getting
+ * us to here.
+ */
+ qemu_log_mask(LOG_GUEST_ERROR, "cadence_uart: TxFIFO overflow");
+ s->r[R_CISR] |= UART_INTR_ROVR;
+ }
+
+ memcpy(s->tx_fifo + s->tx_count, buf, size);
+ s->tx_count += size;
+
+ cadence_uart_xmit(NULL, G_IO_OUT, s);
}
static void uart_receive(void *opaque, const uint8_t *buf, int size)
@@ -337,26 +368,17 @@ static void uart_read_rx_fifo(UartState *s, uint32_t *c)
return;
}
- s->r[R_SR] &= ~UART_SR_INTR_RFUL;
-
if (s->rx_count) {
uint32_t rx_rpos =
(RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
- *c = s->r_fifo[rx_rpos];
+ *c = s->rx_fifo[rx_rpos];
s->rx_count--;
- if (!s->rx_count) {
- s->r[R_SR] |= UART_SR_INTR_REMPTY;
- }
qemu_chr_accept_input(s->chr);
} else {
*c = 0;
- s->r[R_SR] |= UART_SR_INTR_REMPTY;
}
- if (s->rx_count < s->r[R_RTRIG]) {
- s->r[R_SR] &= ~UART_SR_INTR_RTRIG;
- }
uart_update_status(s);
}
@@ -401,6 +423,7 @@ static void uart_write(void *opaque, hwaddr offset,
uart_parameters_setup(s);
break;
}
+ uart_update_status(s);
}
static uint64_t uart_read(void *opaque, hwaddr offset,
@@ -428,8 +451,10 @@ static const MemoryRegionOps uart_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void cadence_uart_reset(UartState *s)
+static void cadence_uart_reset(DeviceState *dev)
{
+ UartState *s = CADENCE_UART(dev);
+
s->r[R_CR] = 0x00000128;
s->r[R_IMR] = 0;
s->r[R_CISR] = 0;
@@ -440,8 +465,7 @@ static void cadence_uart_reset(UartState *s)
uart_rx_reset(s);
uart_tx_reset(s);
- s->rx_count = 0;
- s->rx_wpos = 0;
+ uart_update_status(s);
}
static int cadence_uart_init(SysBusDevice *dev)
@@ -455,15 +479,10 @@ static int cadence_uart_init(SysBusDevice *dev)
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
(QEMUTimerCB *)fifo_trigger_update, s);
- s->tx_time_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- (QEMUTimerCB *)uart_tx_write, s);
-
s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
s->chr = qemu_char_get_next_serial();
- cadence_uart_reset(s);
-
if (s->chr) {
qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
uart_event, s);
@@ -483,17 +502,18 @@ static int cadence_uart_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_cadence_uart = {
.name = "cadence_uart",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
.post_load = cadence_uart_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
- VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE),
+ VMSTATE_UINT8_ARRAY(rx_fifo, UartState, RX_FIFO_SIZE),
+ VMSTATE_UINT8_ARRAY(tx_fifo, UartState, RX_FIFO_SIZE),
VMSTATE_UINT32(rx_count, UartState),
+ VMSTATE_UINT32(tx_count, UartState),
VMSTATE_UINT32(rx_wpos, UartState),
VMSTATE_TIMER(fifo_trigger_handle, UartState),
- VMSTATE_TIMER(tx_time_handle, UartState),
VMSTATE_END_OF_LIST()
}
};
@@ -505,6 +525,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data)
sdc->init = cadence_uart_init;
dc->vmsd = &vmstate_cadence_uart;
+ dc->reset = cadence_uart_reset;
}
static const TypeInfo cadence_uart_info = {
diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c
new file mode 100644
index 0000000000..fd8e07713d
--- /dev/null
+++ b/hw/char/digic-uart.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU model of the Canon DIGIC UART block.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This model is based on reverse engineering efforts
+ * made by CHDK (http://chdk.wikia.com) and
+ * Magic Lantern (http://www.magiclantern.fm) projects
+ * contributors.
+ *
+ * See "Serial terminal" docs here:
+ * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers
+ *
+ * The QEMU model of the Milkymist UART block by Michael Walle
+ * is used as a template.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/char.h"
+
+#include "hw/char/digic-uart.h"
+
+enum {
+ ST_RX_RDY = (1 << 0),
+ ST_TX_RDY = (1 << 1),
+};
+
+static uint64_t digic_uart_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ DigicUartState *s = opaque;
+ uint64_t ret = 0;
+
+ addr >>= 2;
+
+ switch (addr) {
+ case R_RX:
+ s->reg_st &= ~(ST_RX_RDY);
+ ret = s->reg_rx;
+ break;
+
+ case R_ST:
+ ret = s->reg_st;
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "digic-uart: read access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ }
+
+ return ret;
+}
+
+static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ DigicUartState *s = opaque;
+ unsigned char ch = value;
+
+ addr >>= 2;
+
+ switch (addr) {
+ case R_TX:
+ if (s->chr) {
+ qemu_chr_fe_write_all(s->chr, &ch, 1);
+ }
+ break;
+
+ case R_ST:
+ /*
+ * Ignore write to R_ST.
+ *
+ * The point is that this register is actively used
+ * during receiving and transmitting symbols,
+ * but we don't know the function of most of bits.
+ *
+ * Ignoring writes to R_ST is only a simplification
+ * of the model. It has no perceptible side effects
+ * for existing guests.
+ */
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "digic-uart: write access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ }
+}
+
+static const MemoryRegionOps uart_mmio_ops = {
+ .read = digic_uart_read,
+ .write = digic_uart_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int uart_can_rx(void *opaque)
+{
+ DigicUartState *s = opaque;
+
+ return !(s->reg_st & ST_RX_RDY);
+}
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ DigicUartState *s = opaque;
+
+ assert(uart_can_rx(opaque));
+
+ s->reg_st |= ST_RX_RDY;
+ s->reg_rx = *buf;
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static void digic_uart_reset(DeviceState *d)
+{
+ DigicUartState *s = DIGIC_UART(d);
+
+ s->reg_rx = 0;
+ s->reg_st = ST_TX_RDY;
+}
+
+static void digic_uart_realize(DeviceState *dev, Error **errp)
+{
+ DigicUartState *s = DIGIC_UART(dev);
+
+ s->chr = qemu_char_get_next_serial();
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+ }
+}
+
+static void digic_uart_init(Object *obj)
+{
+ DigicUartState *s = DIGIC_UART(obj);
+
+ memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s,
+ TYPE_DIGIC_UART, 0x18);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region);
+}
+
+static const VMStateDescription vmstate_digic_uart = {
+ .name = "digic-uart",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_rx, DigicUartState),
+ VMSTATE_UINT32(reg_st, DigicUartState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void digic_uart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = digic_uart_realize;
+ dc->reset = digic_uart_reset;
+ dc->vmsd = &vmstate_digic_uart;
+}
+
+static const TypeInfo digic_uart_info = {
+ .name = TYPE_DIGIC_UART,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(DigicUartState),
+ .instance_init = digic_uart_init,
+ .class_init = digic_uart_class_init,
+};
+
+static void digic_uart_register_types(void)
+{
+ type_register_static(&digic_uart_info);
+}
+
+type_init(digic_uart_register_types)
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index eef23a0ccc..19b59ccddb 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -192,10 +192,9 @@ typedef struct Exynos4210UartState {
static const char *exynos4210_uart_regname(hwaddr offset)
{
- int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg);
int i;
- for (i = 0; i < regs_number; i++) {
+ for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) {
if (offset == exynos4210_uart_regs[i].offset) {
return exynos4210_uart_regs[i].name;
}
@@ -544,10 +543,9 @@ static void exynos4210_uart_event(void *opaque, int event)
static void exynos4210_uart_reset(DeviceState *dev)
{
Exynos4210UartState *s = EXYNOS4210_UART(dev);
- int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg);
int i;
- for (i = 0; i < regs_number; i++) {
+ for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) {
s->reg[I_(exynos4210_uart_regs[i].offset)] =
exynos4210_uart_regs[i].reset_value;
}
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 703f026370..226e9f9a3c 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -889,22 +889,24 @@ static int virtser_port_qdev_exit(DeviceState *qdev)
return 0;
}
-static int virtio_serial_device_init(VirtIODevice *vdev)
+static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
{
- DeviceState *qdev = DEVICE(vdev);
- VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSerial *vser = VIRTIO_SERIAL(dev);
+ BusState *bus;
uint32_t i, max_supported_ports;
if (!vser->serial.max_virtserial_ports) {
- return -1;
+ error_setg(errp, "Maximum number of serial ports not specified");
+ return;
}
/* Each port takes 2 queues, and one pair is for the control queue */
max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1;
if (vser->serial.max_virtserial_ports > max_supported_ports) {
- error_report("maximum ports supported: %u", max_supported_ports);
- return -1;
+ error_setg(errp, "maximum ports supported: %u", max_supported_ports);
+ return;
}
virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE,
@@ -912,8 +914,9 @@ static int virtio_serial_device_init(VirtIODevice *vdev)
/* Spawn a new virtio-serial bus on which the ports will ride as devices */
qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS,
- qdev, vdev->bus_name);
- vser->bus.qbus.allow_hotplug = 1;
+ dev, vdev->bus_name);
+ bus = BUS(&vser->bus);
+ bus->allow_hotplug = 1;
vser->bus.vser = vser;
QTAILQ_INIT(&vser->ports);
@@ -961,10 +964,8 @@ static int virtio_serial_device_init(VirtIODevice *vdev)
* Register for the savevm section with the virtio-console name
* to preserve backward compat
*/
- register_savevm(qdev, "virtio-console", -1, 3, virtio_serial_save,
+ register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save,
virtio_serial_load, vser);
-
- return 0;
}
static void virtio_serial_port_class_init(ObjectClass *klass, void *data)
@@ -987,10 +988,10 @@ static const TypeInfo virtio_serial_port_type_info = {
.class_init = virtio_serial_port_class_init,
};
-static int virtio_serial_device_exit(DeviceState *dev)
+static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIOSerial *vser = VIRTIO_SERIAL(dev);
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSerial *vser = VIRTIO_SERIAL(dev);
unregister_savevm(dev, "virtio-console", vser);
@@ -1004,7 +1005,6 @@ static int virtio_serial_device_exit(DeviceState *dev)
g_free(vser->post_load);
}
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_serial_properties[] = {
@@ -1016,10 +1016,11 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_serial_device_exit;
+
dc->props = virtio_serial_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
- vdc->init = virtio_serial_device_init;
+ vdc->realize = virtio_serial_device_realize;
+ vdc->unrealize = virtio_serial_device_unrealize;
vdc->get_features = get_features;
vdc->get_config = get_config;
vdc->set_config = set_config;
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 729efa81a2..3f29b49ca4 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -352,21 +352,17 @@ void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
void qdev_prop_set_chr(DeviceState *dev, const char *name,
CharDriverState *value)
{
- Error *errp = NULL;
assert(!value || value->label);
object_property_set_str(OBJECT(dev),
- value ? value->label : "", name, &errp);
- assert_no_error(errp);
+ value ? value->label : "", name, &error_abort);
}
void qdev_prop_set_netdev(DeviceState *dev, const char *name,
NetClientState *value)
{
- Error *errp = NULL;
assert(!value || value->name);
object_property_set_str(OBJECT(dev),
- value ? value->name : "", name, &errp);
- assert_no_error(errp);
+ value ? value->name : "", name, &error_abort);
}
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index dc8ae6958c..b949f0e42a 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -1003,73 +1003,55 @@ void qdev_prop_parse(DeviceState *dev, const char *name, const char *value,
void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
{
- Error *errp = NULL;
- object_property_set_bool(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_bool(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
{
- Error *errp = NULL;
- object_property_set_int(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_int(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
{
- Error *errp = NULL;
- object_property_set_int(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_int(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
{
- Error *errp = NULL;
- object_property_set_int(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_int(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
{
- Error *errp = NULL;
- object_property_set_int(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_int(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
{
- Error *errp = NULL;
- object_property_set_int(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_int(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
{
- Error *errp = NULL;
- object_property_set_str(OBJECT(dev), value, name, &errp);
- assert_no_error(errp);
+ object_property_set_str(OBJECT(dev), value, name, &error_abort);
}
void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
{
- Error *errp = NULL;
char str[2 * 6 + 5 + 1];
snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
value[0], value[1], value[2], value[3], value[4], value[5]);
- object_property_set_str(OBJECT(dev), str, name, &errp);
- assert_no_error(errp);
+ object_property_set_str(OBJECT(dev), str, name, &error_abort);
}
void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
{
Property *prop;
- Error *errp = NULL;
prop = qdev_prop_find(dev, name);
object_property_set_str(OBJECT(dev), prop->info->enum_table[value],
- name, &errp);
- assert_no_error(errp);
+ name, &error_abort);
}
void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
@@ -1161,12 +1143,10 @@ static void set_size(Object *obj, Visitor *v, void *opaque,
static int parse_size(DeviceState *dev, Property *prop, const char *str)
{
uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
- Error *errp = NULL;
if (str != NULL) {
- parse_option_size(prop->name, str, ptr, &errp);
+ parse_option_size(prop->name, str, ptr, &error_abort);
}
- assert_no_error(errp);
return 0;
}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index e374a9399f..82a9123038 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -233,19 +233,19 @@ static int qbus_reset_one(BusState *bus, void *opaque)
{
BusClass *bc = BUS_GET_CLASS(bus);
if (bc->reset) {
- return bc->reset(bus);
+ bc->reset(bus);
}
return 0;
}
void qdev_reset_all(DeviceState *dev)
{
- qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+ qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
void qbus_reset_all(BusState *bus)
{
- qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+ qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
void qbus_reset_all_fn(void *opaque)
@@ -337,49 +337,70 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
return NULL;
}
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque)
+int qbus_walk_children(BusState *bus,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
{
BusChild *kid;
int err;
- if (busfn) {
- err = busfn(bus, opaque);
+ if (pre_busfn) {
+ err = pre_busfn(bus, opaque);
if (err) {
return err;
}
}
QTAILQ_FOREACH(kid, &bus->children, sibling) {
- err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+ err = qdev_walk_children(kid->child,
+ pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
if (err < 0) {
return err;
}
}
+ if (post_busfn) {
+ err = post_busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
return 0;
}
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque)
+int qdev_walk_children(DeviceState *dev,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque)
{
BusState *bus;
int err;
- if (devfn) {
- err = devfn(dev, opaque);
+ if (pre_devfn) {
+ err = pre_devfn(dev, opaque);
if (err) {
return err;
}
}
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
- err = qbus_walk_children(bus, devfn, busfn, opaque);
+ err = qbus_walk_children(bus, pre_devfn, pre_busfn,
+ post_devfn, post_busfn, opaque);
if (err < 0) {
return err;
}
}
+ if (post_devfn) {
+ err = post_devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
return 0;
}
@@ -481,11 +502,6 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
return bus;
}
-void qbus_free(BusState *bus)
-{
- object_unparent(OBJECT(bus));
-}
-
static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
{
BusClass *bc = BUS_GET_CLASS(bus);
@@ -656,14 +672,13 @@ void qdev_property_add_static(DeviceState *dev, Property *prop,
}
if (prop->qtype == QTYPE_QBOOL) {
- object_property_set_bool(obj, prop->defval, prop->name, &local_err);
+ object_property_set_bool(obj, prop->defval, prop->name, &error_abort);
} else if (prop->info->enum_table) {
object_property_set_str(obj, prop->info->enum_table[prop->defval],
- prop->name, &local_err);
+ prop->name, &error_abort);
} else if (prop->qtype == QTYPE_QINT) {
- object_property_set_int(obj, prop->defval, prop->name, &local_err);
+ object_property_set_int(obj, prop->defval, prop->name, &error_abort);
}
- assert_no_error(local_err);
}
static bool device_get_realized(Object *obj, Error **err)
@@ -723,7 +738,6 @@ static void device_initfn(Object *obj)
DeviceState *dev = DEVICE(obj);
ObjectClass *class;
Property *prop;
- Error *err = NULL;
if (qdev_hotplug) {
dev->hotplugged = 1;
@@ -739,31 +753,19 @@ static void device_initfn(Object *obj)
class = object_get_class(OBJECT(dev));
do {
for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
- qdev_property_add_legacy(dev, prop, &err);
- assert_no_error(err);
- qdev_property_add_static(dev, prop, &err);
- assert_no_error(err);
+ qdev_property_add_legacy(dev, prop, &error_abort);
+ qdev_property_add_static(dev, prop, &error_abort);
}
class = object_class_get_parent(class);
} while (class != object_class_by_name(TYPE_DEVICE));
- if (err != NULL) {
- qerror_report_err(err);
- error_free(err);
- exit(1);
- }
object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
- (Object **)&dev->parent_bus, &err);
- assert_no_error(err);
+ (Object **)&dev->parent_bus, &error_abort);
}
static void device_post_init(Object *obj)
{
- DeviceState *dev = DEVICE(obj);
- Error *err = NULL;
-
- qdev_prop_set_globals(dev, &err);
- assert_no_error(err);
+ qdev_prop_set_globals(DEVICE(obj), &error_abort);
}
/* Unlink device from bus and free the structure. */
@@ -794,7 +796,7 @@ static void device_unparent(Object *obj)
while (dev->num_child_bus) {
bus = QLIST_FIRST(&dev->child_bus);
- qbus_free(bus);
+ object_unparent(OBJECT(bus));
}
if (dev->realized) {
object_property_set_bool(obj, false, "realized", NULL);
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index 146f50aa15..f4e760d6eb 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -257,6 +257,13 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data)
DeviceClass *k = DEVICE_CLASS(klass);
k->init = sysbus_device_init;
k->bus_type = TYPE_SYSTEM_BUS;
+ /*
+ * device_add plugs devices into suitable bus. For "real" buses,
+ * that actually connects the device. For sysbus, the connections
+ * need to be made separately, and device_add can't do that. The
+ * device would be left unconnected, and could not possibly work.
+ */
+ k->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo sysbus_device_type_info = {
diff --git a/hw/cpu/icc_bus.c b/hw/cpu/icc_bus.c
index 9a4ea7e2df..7f44c59b25 100644
--- a/hw/cpu/icc_bus.c
+++ b/hw/cpu/icc_bus.c
@@ -43,15 +43,13 @@ static const TypeInfo icc_bus_info = {
static void icc_device_realize(DeviceState *dev, Error **errp)
{
- ICCDevice *id = ICC_DEVICE(dev);
- ICCDeviceClass *idc = ICC_DEVICE_GET_CLASS(id);
-
- if (idc->init) {
- if (idc->init(id) < 0) {
- error_setg(errp, "%s initialization failed.",
- object_get_typename(OBJECT(dev)));
- }
+ ICCDeviceClass *idc = ICC_DEVICE_GET_CLASS(dev);
+
+ /* convert to QOM */
+ if (idc->realize) {
+ idc->realize(dev, errp);
}
+
}
static void icc_device_class_init(ObjectClass *oc, void *data)
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 790e5108ed..ab689e9aae 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -496,7 +496,6 @@ static void pl110_class_init(ObjectClass *klass, void *data)
k->init = pl110_initfn;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
- dc->no_user = 1;
dc->vmsd = &vmstate_pl110;
}
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index efdefd6622..e4f172e3fb 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1144,8 +1144,14 @@ static void qxl_soft_reset(PCIQXLDevice *d)
static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
{
+ bool startstop = qemu_spice_display_is_running(&d->ssd);
+
trace_qxl_hard_reset(d->id, loadvm);
+ if (startstop) {
+ qemu_spice_display_stop();
+ }
+
qxl_spice_reset_cursor(d);
qxl_spice_reset_image_cache(d);
qxl_reset_surfaces(d);
@@ -1159,6 +1165,10 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
}
qemu_spice_create_host_memslot(&d->ssd);
qxl_soft_reset(d);
+
+ if (startstop) {
+ qemu_spice_display_start();
+ }
}
static void qxl_reset_handler(DeviceState *dev)
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c
index 35b90155a2..cb7bda9803 100644
--- a/hw/dma/pl080.c
+++ b/hw/dma/pl080.c
@@ -381,7 +381,6 @@ static void pl080_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- dc->no_user = 1;
dc->vmsd = &vmstate_pl080;
}
diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c
index 2a92ffb82e..eac338f1bc 100644
--- a/hw/dma/sparc32_dma.c
+++ b/hw/dma/sparc32_dma.c
@@ -304,6 +304,8 @@ static void sparc32_dma_class_init(ObjectClass *klass, void *data)
dc->reset = dma_reset;
dc->vmsd = &vmstate_dma;
dc->props = sparc32_dma_properties;
+ /* Reason: pointer property "iommu_opaque" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo sparc32_dma_info = {
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index d67c5f19a4..19f07b3b25 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -569,26 +569,21 @@ static void xilinx_axidma_init(Object *obj)
{
XilinxAXIDMA *s = XILINX_AXI_DMA(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- Error *errp = NULL;
object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
- (Object **) &s->tx_data_dev, &errp);
- assert_no_error(errp);
+ (Object **)&s->tx_data_dev, &error_abort);
object_property_add_link(obj, "axistream-control-connected",
TYPE_STREAM_SLAVE,
- (Object **) &s->tx_control_dev, &errp);
- assert_no_error(errp);
+ (Object **)&s->tx_control_dev, &error_abort);
object_initialize(&s->rx_data_dev, sizeof(s->rx_data_dev),
TYPE_XILINX_AXI_DMA_DATA_STREAM);
object_initialize(&s->rx_control_dev, sizeof(s->rx_control_dev),
TYPE_XILINX_AXI_DMA_CONTROL_STREAM);
object_property_add_child(OBJECT(s), "axistream-connected-target",
- (Object *)&s->rx_data_dev, &errp);
- assert_no_error(errp);
+ (Object *)&s->rx_data_dev, &error_abort);
object_property_add_child(OBJECT(s), "axistream-control-connected-target",
- (Object *)&s->rx_control_dev, &errp);
- assert_no_error(errp);
+ (Object *)&s->rx_control_dev, &error_abort);
sysbus_init_irq(sbd, &s->streams[0].irq);
sysbus_init_irq(sbd, &s->streams[1].irq);
diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c
index b8f572bb70..938782a45d 100644
--- a/hw/gpio/omap_gpio.c
+++ b/hw/gpio/omap_gpio.c
@@ -759,6 +759,8 @@ static void omap_gpio_class_init(ObjectClass *klass, void *data)
k->init = omap_gpio_init;
dc->reset = omap_gpif_reset;
dc->props = omap_gpio_properties;
+ /* Reason: pointer property "clk" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo omap_gpio_info = {
@@ -788,6 +790,8 @@ static void omap2_gpio_class_init(ObjectClass *klass, void *data)
k->init = omap2_gpio_init;
dc->reset = omap2_gpif_reset;
dc->props = omap2_gpio_properties;
+ /* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo omap2_gpio_info = {
diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c
index f528b2b38e..2d8e2b7839 100644
--- a/hw/i2c/omap_i2c.c
+++ b/hw/i2c/omap_i2c.c
@@ -475,6 +475,8 @@ static void omap_i2c_class_init(ObjectClass *klass, void *data)
k->init = omap_i2c_init;
dc->props = omap_i2c_properties;
dc->reset = omap_i2c_reset;
+ /* Reason: pointer properties "iclk", "fclk" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo omap_i2c_info = {
diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c
index 0154283762..0218f8a0eb 100644
--- a/hw/i2c/smbus_eeprom.c
+++ b/hw/i2c/smbus_eeprom.c
@@ -121,6 +121,8 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
sc->write_data = eeprom_write_data;
sc->read_data = eeprom_read_data;
dc->props = smbus_eeprom_properties;
+ /* Reason: pointer property "data" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo smbus_eeprom_info = {
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
index ca229789f4..8d47eaffc8 100644
--- a/hw/i2c/smbus_ich9.c
+++ b/hw/i2c/smbus_ich9.c
@@ -97,11 +97,15 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6;
k->revision = ICH9_A2_SMB_REVISION;
k->class_id = PCI_CLASS_SERIAL_SMBUS;
- dc->no_user = 1;
dc->vmsd = &vmstate_ich9_smbus;
dc->desc = "ICH9 SMBUS Bridge";
k->init = ich9_smbus_initfn;
k->config_write = ich9_smbus_write_config;
+ /*
+ * Reason: part of ICH9 southbridge, needs to be wired up by
+ * pc_q35_init()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index befc39f253..48312f5a83 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -924,10 +924,16 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
static void
build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
{
- void *dsdt;
+ AcpiTableHeader *dsdt;
+
assert(misc->dsdt_code && misc->dsdt_size);
+
dsdt = acpi_data_push(table_data, misc->dsdt_size);
memcpy(dsdt, misc->dsdt_code, misc->dsdt_size);
+
+ memset(dsdt, 0, sizeof *dsdt);
+ build_header(linker, table_data, dsdt, ACPI_DSDT_SIGNATURE,
+ misc->dsdt_size, 1);
}
/* Build final rsdt table */
diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl
index c96ac42a31..995b415bae 100644
--- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl
+++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl
@@ -52,7 +52,6 @@ Scope(\_SB) {
Sleep(200)
}
- /* CPU hotplug notify method */
OperationRegion(PRST, SystemIO, 0xaf00, 32)
Field(PRST, ByteAcc, NoLock, Preserve) {
PRS, 256
diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
index 90efce0d18..a377424f39 100644
--- a/hw/i386/acpi-dsdt.dsl
+++ b/hw/i386/acpi-dsdt.dsl
@@ -235,7 +235,7 @@ DefinitionBlock (
}
Return (0x0B)
}
- Method(IQCR, 1, NotSerialized) {
+ Method(IQCR, 1, Serialized) {
// _CRS method - get current settings
Name(PRR0, ResourceTemplate() {
Interrupt(, Level, ActiveHigh, Shared) { 0 }
diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated
index 2c011070c4..f8bd4ea1b5 100644
--- a/hw/i386/acpi-dsdt.hex.generated
+++ b/hw/i386/acpi-dsdt.hex.generated
@@ -8,7 +8,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x0,
0x0,
0x1,
-0xe0,
+0xd8,
0x42,
0x58,
0x50,
@@ -3379,7 +3379,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x51,
0x43,
0x52,
-0x1,
+0x9,
0x8,
0x50,
0x52,
diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c
index 5609063120..e873b509a5 100644
--- a/hw/i386/kvm/apic.c
+++ b/hw/i386/kvm/apic.c
@@ -25,9 +25,9 @@ static inline uint32_t kvm_apic_get_reg(struct kvm_lapic_state *kapic,
return *((uint32_t *)(kapic->regs + (reg_id << 4)));
}
-void kvm_put_apic_state(DeviceState *d, struct kvm_lapic_state *kapic)
+void kvm_put_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
int i;
memset(kapic, 0, sizeof(*kapic));
@@ -51,9 +51,9 @@ void kvm_put_apic_state(DeviceState *d, struct kvm_lapic_state *kapic)
kvm_apic_set_reg(kapic, 0x3e, s->divide_conf);
}
-void kvm_get_apic_state(DeviceState *d, struct kvm_lapic_state *kapic)
+void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
int i, v;
s->id = kvm_apic_get_reg(kapic, 0x2) >> 24;
@@ -171,8 +171,10 @@ static const MemoryRegionOps kvm_apic_io_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void kvm_apic_init(APICCommonState *s)
+static void kvm_apic_realize(DeviceState *dev, Error **errp)
{
+ APICCommonState *s = APIC_COMMON(dev);
+
memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi",
APIC_SPACE_SIZE);
@@ -185,7 +187,7 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
- k->init = kvm_apic_init;
+ k->realize = kvm_apic_realize;
k->set_base = kvm_apic_set_base;
k->set_tpr = kvm_apic_set_tpr;
k->get_tpr = kvm_apic_get_tpr;
diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
index 383938d1bc..892aa025f4 100644
--- a/hw/i386/kvm/clock.c
+++ b/hw/i386/kvm/clock.c
@@ -114,7 +114,6 @@ static void kvmclock_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = kvmclock_realize;
- dc->no_user = 1;
dc->vmsd = &kvmclock_vmsd;
}
diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c
index f11a540825..d2a6c4cf60 100644
--- a/hw/i386/kvm/ioapic.c
+++ b/hw/i386/kvm/ioapic.c
@@ -127,11 +127,13 @@ static void kvm_ioapic_set_irq(void *opaque, int irq, int level)
apic_report_irq_delivered(delivered);
}
-static void kvm_ioapic_init(IOAPICCommonState *s, int instance_no)
+static void kvm_ioapic_realize(DeviceState *dev, Error **errp)
{
+ IOAPICCommonState *s = IOAPIC_COMMON(dev);
+
memory_region_init_reservation(&s->io_memory, NULL, "kvm-ioapic", 0x1000);
- qdev_init_gpio_in(DEVICE(s), kvm_ioapic_set_irq, IOAPIC_NUM_PINS);
+ qdev_init_gpio_in(dev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS);
}
static Property kvm_ioapic_properties[] = {
@@ -144,7 +146,7 @@ static void kvm_ioapic_class_init(ObjectClass *klass, void *data)
IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- k->init = kvm_ioapic_init;
+ k->realize = kvm_ioapic_realize;
k->pre_save = kvm_ioapic_get;
k->post_load = kvm_ioapic_put;
dc->reset = kvm_ioapic_reset;
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 2d876009fc..72025d0359 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -366,7 +366,7 @@ static int vapic_enable(VAPICROMState *s, X86CPU *cpu)
(((hwaddr)cpu_number) << VAPIC_CPU_SHIFT);
cpu_physical_memory_rw(vapic_paddr + offsetof(VAPICState, enabled),
(void *)&enabled, sizeof(enabled), 1);
- apic_enable_vapic(cpu->env.apic_state, vapic_paddr);
+ apic_enable_vapic(cpu->apic_state, vapic_paddr);
s->state = VAPIC_ACTIVE;
@@ -496,12 +496,10 @@ static void vapic_enable_tpr_reporting(bool enable)
};
CPUState *cs;
X86CPU *cpu;
- CPUX86State *env;
CPU_FOREACH(cs) {
cpu = X86_CPU(cs);
- env = &cpu->env;
- info.apic = env->apic_state;
+ info.apic = cpu->apic_state;
run_on_cpu(cs, vapic_do_enable_tpr_reporting, &info);
}
}
@@ -700,7 +698,7 @@ static void vapic_write(void *opaque, hwaddr addr, uint64_t data,
default:
case 4:
if (!kvm_irqchip_in_kernel()) {
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(cpu->apic_state);
}
break;
}
@@ -827,7 +825,6 @@ static void vapic_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->no_user = 1;
dc->reset = vapic_reset;
dc->vmsd = &vmstate_vapic;
dc->realize = vapic_realize;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 12c436e7f1..6f0be37d8b 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -171,14 +171,15 @@ void cpu_smm_update(CPUX86State *env)
/* IRQ handling */
int cpu_get_pic_interrupt(CPUX86State *env)
{
+ X86CPU *cpu = x86_env_get_cpu(env);
int intno;
- intno = apic_get_interrupt(env->apic_state);
+ intno = apic_get_interrupt(cpu->apic_state);
if (intno >= 0) {
return intno;
}
/* read the irq from the PIC */
- if (!apic_accept_pic_intr(env->apic_state)) {
+ if (!apic_accept_pic_intr(cpu->apic_state)) {
return -1;
}
@@ -190,15 +191,13 @@ static void pic_irq_request(void *opaque, int irq, int level)
{
CPUState *cs = first_cpu;
X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
- if (env->apic_state) {
+ if (cpu->apic_state) {
CPU_FOREACH(cs) {
cpu = X86_CPU(cs);
- env = &cpu->env;
- if (apic_accept_pic_intr(env->apic_state)) {
- apic_deliver_pic_intr(env->apic_state, level);
+ if (apic_accept_pic_intr(cpu->apic_state)) {
+ apic_deliver_pic_intr(cpu->apic_state, level);
}
}
} else {
@@ -547,10 +546,15 @@ static void port92_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->no_user = 1;
dc->realize = port92_realizefn;
dc->reset = port92_reset;
dc->vmsd = &vmstate_port92_isa;
+ /*
+ * Reason: unlike ordinary ISA devices, this one needs additional
+ * wiring: its A20 output line needs to be wired up by
+ * port92_init().
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo port92_info = {
@@ -908,7 +912,7 @@ DeviceState *cpu_get_current_apic(void)
{
if (current_cpu) {
X86CPU *cpu = X86_CPU(current_cpu);
- return cpu->env.apic_state;
+ return cpu->apic_state;
} else {
return NULL;
}
@@ -1002,7 +1006,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
}
/* map APIC MMIO area if CPU has APIC */
- if (cpu && cpu->env.apic_state) {
+ if (cpu && cpu->apic_state) {
/* XXX: what if the base changes? */
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0,
APIC_DEFAULT_ADDRESS, 0x1000);
@@ -1093,21 +1097,13 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
return guest_info;
}
-void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start,
- uint64_t pci_hole64_size)
+/* setup pci memory address space mapping into system address space */
+void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
+ MemoryRegion *pci_address_space)
{
- if ((sizeof(hwaddr) == 4) || (!pci_hole64_size)) {
- return;
- }
- /*
- * BIOS does not set MTRR entries for the 64 bit window, so no need to
- * align address to power of two. Align address at 1G, this makes sure
- * it can be exactly covered with a PAT entry even when using huge
- * pages.
- */
- pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
- pci_info->w64.end = pci_info->w64.begin + pci_hole64_size;
- assert(pci_info->w64.begin <= pci_info->w64.end);
+ /* Set to lower priority than RAM */
+ memory_region_add_subregion_overlap(system_memory, 0x0,
+ pci_address_space, -1);
}
void pc_acpi_init(const char *default_dsdt)
@@ -1261,7 +1257,8 @@ static const MemoryRegionOps ioportF0_io_ops = {
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
ISADevice **floppy,
- bool no_vmport)
+ bool no_vmport,
+ uint32 hpet_irqs)
{
int i;
DriveInfo *fd[MAX_FD];
@@ -1288,9 +1285,21 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
* when the HPET wants to take over. Thus we have to disable the latter.
*/
if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) {
- hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL);
-
+ /* In order to set property, here not using sysbus_try_create_simple */
+ hpet = qdev_try_create(NULL, TYPE_HPET);
if (hpet) {
+ /* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7
+ * and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23,
+ * IRQ8 and IRQ2.
+ */
+ uint8_t compat = object_property_get_int(OBJECT(hpet),
+ HPET_INTCAP, NULL);
+ if (!compat) {
+ qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs);
+ }
+ qdev_init_nofail(hpet);
+ sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE);
+
for (i = 0; i < GSI_NUM_PINS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]);
}
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index ab562853b8..a327d71fb1 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -28,6 +28,7 @@
#include "hw/loader.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic.h"
+#include "hw/i386/smbios.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_ids.h"
#include "hw/usb.h"
@@ -59,6 +60,12 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
static bool has_pci_info;
static bool has_acpi_build = true;
+static bool smbios_type1_defaults = true;
+/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
+ * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
+ * pages in the host.
+ */
+static bool gigabyte_align = true;
/* PC hardware initialisation */
static void pc_init1(QEMUMachineInitArgs *args,
@@ -104,9 +111,17 @@ static void pc_init1(QEMUMachineInitArgs *args,
kvmclock_create();
}
+ /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory).
+ * If it doesn't, we need to split it in chunks below and above 4G.
+ * In any case, try to make sure that guest addresses aligned at
+ * 1G boundaries get mapped to host addresses aligned at 1G boundaries.
+ * For old machine types, use whatever split we used historically to avoid
+ * breaking migration.
+ */
if (args->ram_size >= 0xe0000000) {
- above_4g_mem_size = args->ram_size - 0xe0000000;
- below_4g_mem_size = 0xe0000000;
+ ram_addr_t lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
+ above_4g_mem_size = args->ram_size - lowmem;
+ below_4g_mem_size = lowmem;
} else {
above_4g_mem_size = 0;
below_4g_mem_size = args->ram_size;
@@ -114,7 +129,7 @@ static void pc_init1(QEMUMachineInitArgs *args,
if (pci_enabled) {
pci_memory = g_new(MemoryRegion, 1);
- memory_region_init(pci_memory, NULL, "pci", INT64_MAX);
+ memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
rom_memory = pci_memory;
} else {
pci_memory = NULL;
@@ -128,6 +143,12 @@ static void pc_init1(QEMUMachineInitArgs *args,
guest_info->has_pci_info = has_pci_info;
guest_info->isapc_ram_fw = !pci_enabled;
+ if (smbios_type1_defaults) {
+ /* These values are guest ABI, do not change */
+ smbios_set_type1_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
+ args->machine->name);
+ }
+
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
fw_cfg = pc_memory_init(system_memory,
@@ -150,7 +171,6 @@ static void pc_init1(QEMUMachineInitArgs *args,
pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,
system_memory, system_io, args->ram_size,
below_4g_mem_size,
- 0x100000000ULL - below_4g_mem_size,
above_4g_mem_size,
pci_memory, ram_memory);
} else {
@@ -183,7 +203,8 @@ static void pc_init1(QEMUMachineInitArgs *args,
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled());
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, xen_enabled(),
+ 0x4);
pc_nic_init(isa_bus, pci_bus);
@@ -235,8 +256,15 @@ static void pc_init_pci(QEMUMachineInitArgs *args)
pc_init1(args, 1, 1);
}
+static void pc_compat_1_7(QEMUMachineInitArgs *args)
+{
+ smbios_type1_defaults = false;
+ gigabyte_align = false;
+}
+
static void pc_compat_1_6(QEMUMachineInitArgs *args)
{
+ pc_compat_1_7(args);
has_pci_info = false;
rom_file_in_ram = false;
has_acpi_build = false;
@@ -267,6 +295,12 @@ static void pc_compat_1_2(QEMUMachineInitArgs *args)
disable_kvm_pv_eoi();
}
+static void pc_init_pci_1_7(QEMUMachineInitArgs *args)
+{
+ pc_compat_1_7(args);
+ pc_init_pci(args);
+}
+
static void pc_init_pci_1_6(QEMUMachineInitArgs *args)
{
pc_compat_1_6(args);
@@ -303,6 +337,7 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args)
{
has_pci_info = false;
has_acpi_build = false;
+ smbios_type1_defaults = false;
disable_kvm_pv_eoi();
enable_compat_apic_id_mode();
pc_init1(args, 1, 0);
@@ -312,6 +347,7 @@ static void pc_init_isa(QEMUMachineInitArgs *args)
{
has_pci_info = false;
has_acpi_build = false;
+ smbios_type1_defaults = false;
if (!args->cpu_model) {
args->cpu_model = "486";
}
@@ -356,7 +392,11 @@ static QEMUMachine pc_i440fx_machine_v2_0 = {
static QEMUMachine pc_i440fx_machine_v1_7 = {
PC_I440FX_1_7_MACHINE_OPTIONS,
.name = "pc-i440fx-1.7",
- .init = pc_init_pci,
+ .init = pc_init_pci_1_7,
+ .compat_props = (GlobalProperty[]) {
+ PC_COMPAT_1_7,
+ { /* end of list */ }
+ },
};
#define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 97aa84264c..07f38ff704 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -39,6 +39,7 @@
#include "hw/pci-host/q35.h"
#include "exec/address-spaces.h"
#include "hw/i386/ich9.h"
+#include "hw/i386/smbios.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci.h"
#include "hw/usb.h"
@@ -49,6 +50,7 @@
static bool has_pci_info;
static bool has_acpi_build = true;
+static bool smbios_type1_defaults = true;
/* PC hardware initialisation */
static void pc_q35_init(QEMUMachineInitArgs *args)
@@ -101,7 +103,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
/* pci enabled */
if (pci_enabled) {
pci_memory = g_new(MemoryRegion, 1);
- memory_region_init(pci_memory, NULL, "pci", INT64_MAX);
+ memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
rom_memory = pci_memory;
} else {
pci_memory = NULL;
@@ -113,6 +115,12 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
guest_info->isapc_ram_fw = false;
guest_info->has_acpi_build = has_acpi_build;
+ if (smbios_type1_defaults) {
+ /* These values are guest ABI, do not change */
+ smbios_set_type1_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
+ args->machine->name);
+ }
+
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
pc_memory_init(get_system_memory(),
@@ -182,7 +190,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
pc_register_ferr_irq(gsi[13]);
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false);
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy, false, 0xff0104);
/* connect pm stuff to lpc */
ich9_lpc_pm_init(lpc);
@@ -217,8 +225,14 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
}
}
+static void pc_compat_1_7(QEMUMachineInitArgs *args)
+{
+ smbios_type1_defaults = false;
+}
+
static void pc_compat_1_6(QEMUMachineInitArgs *args)
{
+ pc_compat_1_7(args);
has_pci_info = false;
rom_file_in_ram = false;
has_acpi_build = false;
@@ -236,6 +250,12 @@ static void pc_compat_1_4(QEMUMachineInitArgs *args)
x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
+static void pc_q35_init_1_7(QEMUMachineInitArgs *args)
+{
+ pc_compat_1_7(args);
+ pc_q35_init(args);
+}
+
static void pc_q35_init_1_6(QEMUMachineInitArgs *args)
{
pc_compat_1_6(args);
@@ -275,7 +295,11 @@ static QEMUMachine pc_q35_machine_v2_0 = {
static QEMUMachine pc_q35_machine_v1_7 = {
PC_Q35_1_7_MACHINE_OPTIONS,
.name = "pc-q35-1.7",
- .init = pc_q35_init,
+ .init = pc_q35_init_1_7,
+ .compat_props = (GlobalProperty[]) {
+ PC_Q35_COMPAT_1_7,
+ { /* end of list */ }
+ },
};
#define PC_Q35_1_6_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS
@@ -285,7 +309,7 @@ static QEMUMachine pc_q35_machine_v1_6 = {
.name = "pc-q35-1.6",
.init = pc_q35_init_1_6,
.compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_6,
+ PC_Q35_COMPAT_1_6,
{ /* end of list */ }
},
};
@@ -295,7 +319,7 @@ static QEMUMachine pc_q35_machine_v1_5 = {
.name = "pc-q35-1.5",
.init = pc_q35_init_1_5,
.compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_5,
+ PC_Q35_COMPAT_1_5,
{ /* end of list */ }
},
};
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index e917c83540..75a7ebbaa7 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -72,35 +72,102 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
memory_region_set_readonly(isa_bios, true);
}
-static void pc_system_flash_init(MemoryRegion *rom_memory,
- DriveInfo *pflash_drv)
+#define FLASH_MAP_UNIT_MAX 2
+
+/* We don't have a theoretically justifiable exact lower bound on the base
+ * address of any flash mapping. In practice, the IO-APIC MMIO range is
+ * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
+ * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
+ * size.
+ */
+#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024))
+
+/* This function maps flash drives from 4G downward, in order of their unit
+ * numbers. The mapping starts at unit#0, with unit number increments of 1, and
+ * stops before the first missing flash drive, or before
+ * unit#FLASH_MAP_UNIT_MAX, whichever is reached first.
+ *
+ * Addressing within one flash drive is of course not reversed.
+ *
+ * An error message is printed and the process exits if:
+ * - the size of the backing file for a flash drive is non-positive, or not a
+ * multiple of the required sector size, or
+ * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN.
+ *
+ * The drive with unit#0 (if available) is mapped at the highest address, and
+ * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is
+ * not supported.
+ */
+static void pc_system_flash_init(MemoryRegion *rom_memory)
{
+ int unit;
+ DriveInfo *pflash_drv;
BlockDriverState *bdrv;
int64_t size;
- hwaddr phys_addr;
+ char *fatal_errmsg = NULL;
+ hwaddr phys_addr = 0x100000000ULL;
int sector_bits, sector_size;
pflash_t *system_flash;
MemoryRegion *flash_mem;
+ char name[64];
- bdrv = pflash_drv->bdrv;
- size = bdrv_getlength(pflash_drv->bdrv);
sector_bits = 12;
sector_size = 1 << sector_bits;
- if ((size % sector_size) != 0) {
- fprintf(stderr,
- "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n",
- sector_size);
- exit(1);
+ for (unit = 0;
+ (unit < FLASH_MAP_UNIT_MAX &&
+ (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL);
+ ++unit) {
+ bdrv = pflash_drv->bdrv;
+ size = bdrv_getlength(bdrv);
+ if (size < 0) {
+ fatal_errmsg = g_strdup_printf("failed to get backing file size");
+ } else if (size == 0) {
+ fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
+ "cannot have zero size");
+ } else if ((size % sector_size) != 0) {
+ fatal_errmsg = g_strdup_printf("PC system firmware (pflash) "
+ "must be a multiple of 0x%x", sector_size);
+ } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) {
+ fatal_errmsg = g_strdup_printf("oversized backing file, pflash "
+ "segments cannot be mapped under "
+ TARGET_FMT_plx, FLASH_MAP_BASE_MIN);
+ }
+ if (fatal_errmsg != NULL) {
+ Location loc;
+
+ /* push a new, "none" location on the location stack; overwrite its
+ * contents with the location saved in the option; print the error
+ * (includes location); pop the top
+ */
+ loc_push_none(&loc);
+ if (pflash_drv->opts != NULL) {
+ qemu_opts_loc_restore(pflash_drv->opts);
+ }
+ error_report("%s", fatal_errmsg);
+ loc_pop(&loc);
+ g_free(fatal_errmsg);
+ exit(1);
+ }
+
+ phys_addr -= size;
+
+ /* pflash_cfi01_register() creates a deep copy of the name */
+ snprintf(name, sizeof name, "system.flash%d", unit);
+ system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name,
+ size, bdrv, sector_size,
+ size >> sector_bits,
+ 1 /* width */,
+ 0x0000 /* id0 */,
+ 0x0000 /* id1 */,
+ 0x0000 /* id2 */,
+ 0x0000 /* id3 */,
+ 0 /* be */);
+ if (unit == 0) {
+ flash_mem = pflash_cfi01_get_memory(system_flash);
+ pc_isa_bios_init(rom_memory, flash_mem, size);
+ }
}
-
- phys_addr = 0x100000000ULL - size;
- system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size,
- bdrv, sector_size, size >> sector_bits,
- 1, 0x0000, 0x0000, 0x0000, 0x0000, 0);
- flash_mem = pflash_cfi01_get_memory(system_flash);
-
- pc_isa_bios_init(rom_memory, flash_mem, size);
}
static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
@@ -181,5 +248,5 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
exit(1);
}
- pc_system_flash_init(rom_memory, pflash_drv);
+ pc_system_flash_init(rom_memory);
}
diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 21c89b098b..7934a9ddfb 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -333,7 +333,7 @@ DefinitionBlock (
}
Return (0x0B)
}
- Method(IQCR, 1, NotSerialized) {
+ Method(IQCR, 1, Serialized) {
// _CRS method - get current settings
Name(PRR0, ResourceTemplate() {
Interrupt(, Level, ActiveHigh, Shared) { 0 }
@@ -417,11 +417,11 @@ DefinitionBlock (
Method(_L00) {
}
Method(_L01) {
+ }
+ Method(_E02) {
// CPU hotplug event
\_SB.PRSC()
}
- Method(_L02) {
- }
Method(_L03) {
}
Method(_L04) {
diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated
index 32c16ff86f..111ad3e9c2 100644
--- a/hw/i386/q35-acpi-dsdt.hex.generated
+++ b/hw/i386/q35-acpi-dsdt.hex.generated
@@ -8,7 +8,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x0,
0x0,
0x1,
-0x6,
+0xfe,
0x42,
0x58,
0x50,
@@ -5338,7 +5338,7 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x51,
0x43,
0x52,
-0x1,
+0x9,
0x8,
0x50,
0x52,
diff --git a/hw/i386/smbios.c b/hw/i386/smbios.c
index d3f1ee65c6..e8f41ad435 100644
--- a/hw/i386/smbios.c
+++ b/hw/i386/smbios.c
@@ -256,6 +256,20 @@ static void smbios_build_type_1_fields(void)
}
}
+void smbios_set_type1_defaults(const char *manufacturer,
+ const char *product, const char *version)
+{
+ if (!type1.manufacturer) {
+ type1.manufacturer = manufacturer;
+ }
+ if (!type1.product) {
+ type1.product = product;
+ }
+ if (!type1.version) {
+ type1.version = version;
+ }
+}
+
uint8_t *smbios_get_table(size_t *length)
{
if (!smbios_immutable) {
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e1f4c33fb8..036cd4a6d1 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2103,7 +2103,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
s->smart_selftest_count = 0;
if (kind == IDE_CD) {
bdrv_set_dev_ops(bs, &ide_cd_block_ops, s);
- bdrv_set_buffer_alignment(bs, 2048);
+ bdrv_set_guest_block_size(bs, 2048);
} else {
if (!bdrv_is_inserted(s->bs)) {
error_report("Device needs media, but drive is empty");
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
index ab36749417..9b5960b44e 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -248,7 +248,6 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1;
k->class_id = PCI_CLASS_STORAGE_IDE;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->no_user = 1;
}
static const TypeInfo piix3_ide_info = {
@@ -267,7 +266,6 @@ static void piix3_ide_xen_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1;
k->class_id = PCI_CLASS_STORAGE_IDE;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->no_user = 1;
dc->unplug = pci_piix3_xen_ide_unplug;
}
@@ -289,7 +287,6 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_82371AB;
k->class_id = PCI_CLASS_STORAGE_IDE;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->no_user = 1;
}
static const TypeInfo piix4_ide_info = {
diff --git a/hw/ide/via.c b/hw/ide/via.c
index 99468c773e..198123b026 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -225,7 +225,6 @@ static void via_ide_class_init(ObjectClass *klass, void *data)
k->revision = 0x06;
k->class_id = PCI_CLASS_STORAGE_IDE;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- dc->no_user = 1;
}
static const TypeInfo via_ide_info = {
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index ce86237cf3..655b8c5011 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -522,7 +522,6 @@ static void i8042_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = i8042_realizefn;
- dc->no_user = 1;
dc->vmsd = &vmstate_kbd_isa;
}
diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c
index 846d1370de..b90b0ba102 100644
--- a/hw/input/pxa2xx_keypad.c
+++ b/hw/input/pxa2xx_keypad.c
@@ -85,7 +85,7 @@
struct PXA2xxKeyPadState {
MemoryRegion iomem;
qemu_irq irq;
- struct keymap *map;
+ const struct keymap *map;
int pressed_cnt;
int alt_code;
@@ -322,8 +322,8 @@ PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
return s;
}
-void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
- int size)
+void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
+ const struct keymap *map, int size)
{
if(!map || size < 0x80) {
fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__);
diff --git a/hw/input/vmmouse.c b/hw/input/vmmouse.c
index abd032b794..6a5053352a 100644
--- a/hw/input/vmmouse.c
+++ b/hw/input/vmmouse.c
@@ -282,10 +282,11 @@ static void vmmouse_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = vmmouse_realizefn;
- dc->no_user = 1;
dc->reset = vmmouse_reset;
dc->vmsd = &vmstate_vmmouse;
dc->props = vmmouse_properties;
+ /* Reason: pointer property "ps2_mouse" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo vmmouse_info = {
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 47ac44264c..60eb936e0d 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
+obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c
new file mode 100644
index 0000000000..407d563514
--- /dev/null
+++ b/hw/intc/allwinner-a10-pic.c
@@ -0,0 +1,200 @@
+/*
+ * Allwinner A10 interrupt controller device emulation
+ *
+ * Copyright (C) 2013 Li Guang
+ * Written by Li Guang <lig.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/devices.h"
+#include "sysemu/sysemu.h"
+#include "hw/intc/allwinner-a10-pic.h"
+
+static void aw_a10_pic_update(AwA10PICState *s)
+{
+ uint8_t i;
+ int irq = 0, fiq = 0;
+
+ for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
+ irq |= s->irq_pending[i] & ~s->mask[i];
+ fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i];
+ }
+
+ qemu_set_irq(s->parent_irq, !!irq);
+ qemu_set_irq(s->parent_fiq, !!fiq);
+}
+
+static void aw_a10_pic_set_irq(void *opaque, int irq, int level)
+{
+ AwA10PICState *s = opaque;
+
+ if (level) {
+ set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]);
+ }
+ aw_a10_pic_update(s);
+}
+
+static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AwA10PICState *s = opaque;
+ uint8_t index = (offset & 0xc) / 4;
+
+ switch (offset) {
+ case AW_A10_PIC_VECTOR:
+ return s->vector;
+ case AW_A10_PIC_BASE_ADDR:
+ return s->base_addr;
+ case AW_A10_PIC_PROTECT:
+ return s->protect;
+ case AW_A10_PIC_NMI:
+ return s->nmi;
+ case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
+ return s->irq_pending[index];
+ case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
+ return s->fiq_pending[index];
+ case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
+ return s->select[index];
+ case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
+ return s->enable[index];
+ case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
+ return s->mask[index];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+
+ return 0;
+}
+
+static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ AwA10PICState *s = opaque;
+ uint8_t index = (offset & 0xc) / 4;
+
+ switch (offset) {
+ case AW_A10_PIC_VECTOR:
+ s->vector = value & ~0x3;
+ break;
+ case AW_A10_PIC_BASE_ADDR:
+ s->base_addr = value & ~0x3;
+ case AW_A10_PIC_PROTECT:
+ s->protect = value;
+ break;
+ case AW_A10_PIC_NMI:
+ s->nmi = value;
+ break;
+ case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8:
+ s->irq_pending[index] &= ~value;
+ break;
+ case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8:
+ s->fiq_pending[index] &= ~value;
+ break;
+ case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8:
+ s->select[index] = value;
+ break;
+ case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8:
+ s->enable[index] = value;
+ break;
+ case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8:
+ s->mask[index] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+
+ aw_a10_pic_update(s);
+}
+
+static const MemoryRegionOps aw_a10_pic_ops = {
+ .read = aw_a10_pic_read,
+ .write = aw_a10_pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_aw_a10_pic = {
+ .name = "a10.pic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(vector, AwA10PICState),
+ VMSTATE_UINT32(base_addr, AwA10PICState),
+ VMSTATE_UINT32(protect, AwA10PICState),
+ VMSTATE_UINT32(nmi, AwA10PICState),
+ VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
+ VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM),
+ VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM),
+ VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM),
+ VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void aw_a10_pic_init(Object *obj)
+{
+ AwA10PICState *s = AW_A10_PIC(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+
+ qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_fiq);
+ memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s,
+ TYPE_AW_A10_PIC, 0x400);
+ sysbus_init_mmio(dev, &s->iomem);
+}
+
+static void aw_a10_pic_reset(DeviceState *d)
+{
+ AwA10PICState *s = AW_A10_PIC(d);
+ uint8_t i;
+
+ s->base_addr = 0;
+ s->protect = 0;
+ s->nmi = 0;
+ s->vector = 0;
+ for (i = 0; i < AW_A10_PIC_REG_NUM; i++) {
+ s->irq_pending[i] = 0;
+ s->fiq_pending[i] = 0;
+ s->select[i] = 0;
+ s->enable[i] = 0;
+ s->mask[i] = 0;
+ }
+}
+
+static void aw_a10_pic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = aw_a10_pic_reset;
+ dc->desc = "allwinner a10 pic";
+ dc->vmsd = &vmstate_aw_a10_pic;
+ }
+
+static const TypeInfo aw_a10_pic_info = {
+ .name = TYPE_AW_A10_PIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AwA10PICState),
+ .instance_init = aw_a10_pic_init,
+ .class_init = aw_a10_pic_class_init,
+};
+
+static void aw_a10_register_types(void)
+{
+ type_register_static(&aw_a10_pic_info);
+}
+
+type_init(aw_a10_register_types);
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index a913186ed0..3d3deb6298 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -171,9 +171,9 @@ static void apic_local_deliver(APICCommonState *s, int vector)
}
}
-void apic_deliver_pic_intr(DeviceState *d, int level)
+void apic_deliver_pic_intr(DeviceState *dev, int level)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
if (level) {
apic_local_deliver(s, APIC_LVT_LINT0);
@@ -376,9 +376,9 @@ static void apic_update_irq(APICCommonState *s)
}
}
-void apic_poll_irq(DeviceState *d)
+void apic_poll_irq(DeviceState *dev)
{
- APICCommonState *s = APIC_COMMON(d);
+ APICCommonState *s = APIC_COMMON(dev);
apic_sync_vapic(s, SYNC_FROM_VAPIC);
apic_update_irq(s);
@@ -482,9 +482,9 @@ static void apic_startup(APICCommonState *s, int vector_num)
cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI);
}
-void apic_sipi(DeviceState *d)
+void apic_sipi(DeviceState *dev)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI);
@@ -494,11 +494,11 @@ void apic_sipi(DeviceState *d)
s->wait_for_sipi = 0;
}
-static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode,
+static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
uint8_t delivery_mode, uint8_t vector_num,
uint8_t trigger_mode)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
uint32_t deliver_bitmask[MAX_APIC_WORDS];
int dest_shorthand = (s->icr[0] >> 18) & 3;
APICCommonState *apic_iter;
@@ -551,9 +551,9 @@ static bool apic_check_pic(APICCommonState *s)
return true;
}
-int apic_get_interrupt(DeviceState *d)
+int apic_get_interrupt(DeviceState *dev)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
int intno;
/* if the APIC is installed or enabled, we let the 8259 handle the
@@ -585,9 +585,9 @@ int apic_get_interrupt(DeviceState *d)
return intno;
}
-int apic_accept_pic_intr(DeviceState *d)
+int apic_accept_pic_intr(DeviceState *dev)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
uint32_t lvt0;
if (!s)
@@ -657,16 +657,16 @@ static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val)
static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
{
- DeviceState *d;
+ DeviceState *dev;
APICCommonState *s;
uint32_t val;
int index;
- d = cpu_get_current_apic();
- if (!d) {
+ dev = cpu_get_current_apic();
+ if (!dev) {
return 0;
}
- s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ s = APIC_COMMON(dev);
index = (addr >> 4) & 0xff;
switch(index) {
@@ -752,7 +752,7 @@ static void apic_send_msi(hwaddr addr, uint32_t data)
static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
{
- DeviceState *d;
+ DeviceState *dev;
APICCommonState *s;
int index = (addr >> 4) & 0xff;
if (addr > 0xfff || !index) {
@@ -765,11 +765,11 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
return;
}
- d = cpu_get_current_apic();
- if (!d) {
+ dev = cpu_get_current_apic();
+ if (!dev) {
return;
}
- s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ s = APIC_COMMON(dev);
trace_apic_mem_writel(addr, val);
@@ -810,7 +810,7 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
break;
case 0x30:
s->icr[0] = val;
- apic_deliver(d, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
+ apic_deliver(dev, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
(s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
(s->icr[0] >> 15) & 1);
break;
@@ -871,8 +871,10 @@ static const MemoryRegionOps apic_io_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void apic_init(APICCommonState *s)
+static void apic_realize(DeviceState *dev, Error **errp)
{
+ APICCommonState *s = APIC_COMMON(dev);
+
memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi",
APIC_SPACE_SIZE);
@@ -886,7 +888,7 @@ static void apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
- k->init = apic_init;
+ k->realize = apic_realize;
k->set_base = apic_set_base;
k->set_tpr = apic_set_tpr;
k->get_tpr = apic_get_tpr;
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index a0beb10863..c623fcc6d8 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -27,21 +27,21 @@
static int apic_irq_delivered;
bool apic_report_tpr_access;
-void cpu_set_apic_base(DeviceState *d, uint64_t val)
+void cpu_set_apic_base(DeviceState *dev, uint64_t val)
{
trace_cpu_set_apic_base(val);
- if (d) {
- APICCommonState *s = APIC_COMMON(d);
+ if (dev) {
+ APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
info->set_base(s, val);
}
}
-uint64_t cpu_get_apic_base(DeviceState *d)
+uint64_t cpu_get_apic_base(DeviceState *dev)
{
- if (d) {
- APICCommonState *s = APIC_COMMON(d);
+ if (dev) {
+ APICCommonState *s = APIC_COMMON(dev);
trace_cpu_get_apic_base((uint64_t)s->apicbase);
return s->apicbase;
} else {
@@ -50,39 +50,39 @@ uint64_t cpu_get_apic_base(DeviceState *d)
}
}
-void cpu_set_apic_tpr(DeviceState *d, uint8_t val)
+void cpu_set_apic_tpr(DeviceState *dev, uint8_t val)
{
APICCommonState *s;
APICCommonClass *info;
- if (!d) {
+ if (!dev) {
return;
}
- s = APIC_COMMON(d);
+ s = APIC_COMMON(dev);
info = APIC_COMMON_GET_CLASS(s);
info->set_tpr(s, val);
}
-uint8_t cpu_get_apic_tpr(DeviceState *d)
+uint8_t cpu_get_apic_tpr(DeviceState *dev)
{
APICCommonState *s;
APICCommonClass *info;
- if (!d) {
+ if (!dev) {
return 0;
}
- s = APIC_COMMON(d);
+ s = APIC_COMMON(dev);
info = APIC_COMMON_GET_CLASS(s);
return info->get_tpr(s);
}
-void apic_enable_tpr_access_reporting(DeviceState *d, bool enable)
+void apic_enable_tpr_access_reporting(DeviceState *dev, bool enable)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
apic_report_tpr_access = enable;
@@ -91,19 +91,19 @@ void apic_enable_tpr_access_reporting(DeviceState *d, bool enable)
}
}
-void apic_enable_vapic(DeviceState *d, hwaddr paddr)
+void apic_enable_vapic(DeviceState *dev, hwaddr paddr)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
s->vapic_paddr = paddr;
info->vapic_base_update(s);
}
-void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip,
+void apic_handle_tpr_access_report(DeviceState *dev, target_ulong ip,
TPRAccess access)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access);
}
@@ -129,9 +129,9 @@ int apic_get_irq_delivered(void)
return apic_irq_delivered;
}
-void apic_deliver_nmi(DeviceState *d)
+void apic_deliver_nmi(DeviceState *dev)
{
- APICCommonState *s = APIC_COMMON(d);
+ APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
info->external_nmi(s);
@@ -170,9 +170,9 @@ bool apic_next_timer(APICCommonState *s, int64_t current_time)
return true;
}
-void apic_init_reset(DeviceState *d)
+void apic_init_reset(DeviceState *dev)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
int i;
if (!s) {
@@ -203,19 +203,19 @@ void apic_init_reset(DeviceState *d)
s->timer_expiry = -1;
}
-void apic_designate_bsp(DeviceState *d)
+void apic_designate_bsp(DeviceState *dev)
{
- if (d == NULL) {
+ if (dev == NULL) {
return;
}
- APICCommonState *s = APIC_COMMON(d);
+ APICCommonState *s = APIC_COMMON(dev);
s->apicbase |= MSR_IA32_APICBASE_BSP;
}
-static void apic_reset_common(DeviceState *d)
+static void apic_reset_common(DeviceState *dev)
{
- APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
+ APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
bool bsp;
@@ -226,7 +226,7 @@ static void apic_reset_common(DeviceState *d)
s->vapic_paddr = 0;
info->vapic_base_update(s);
- apic_init_reset(d);
+ apic_init_reset(dev);
if (bsp) {
/*
@@ -284,7 +284,7 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static int apic_init_common(ICCDevice *dev)
+static void apic_common_realize(DeviceState *dev, Error **errp)
{
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info;
@@ -293,14 +293,16 @@ static int apic_init_common(ICCDevice *dev)
static bool mmio_registered;
if (apic_no >= MAX_APICS) {
- return -1;
+ error_setg(errp, "%s initialization failed.",
+ object_get_typename(OBJECT(dev)));
+ return;
}
s->idx = apic_no++;
info = APIC_COMMON_GET_CLASS(s);
- info->init(s);
+ info->realize(dev, errp);
if (!mmio_registered) {
- ICCBus *b = ICC_BUS(qdev_get_parent_bus(DEVICE(dev)));
+ ICCBus *b = ICC_BUS(qdev_get_parent_bus(dev));
memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory);
mmio_registered = true;
}
@@ -315,7 +317,6 @@ static int apic_init_common(ICCDevice *dev)
info->enable_tpr_reporting(s, true);
}
- return 0;
}
static void apic_dispatch_pre_save(void *opaque)
@@ -386,9 +387,13 @@ static void apic_common_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
- dc->no_user = 1;
dc->props = apic_properties_common;
- idc->init = apic_init_common;
+ idc->realize = apic_common_realize;
+ /*
+ * Reason: APIC and CPU need to be wired up by
+ * x86_cpu_apic_create()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo apic_common_type = {
@@ -400,9 +405,9 @@ static const TypeInfo apic_common_type = {
.abstract = true,
};
-static void register_types(void)
+static void apic_common_register_types(void)
{
type_register_static(&apic_common_type);
}
-type_init(register_types)
+type_init(apic_common_register_types)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index d431b7a881..9409684ce8 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -128,7 +128,7 @@ static void gic_set_irq(void *opaque, int irq, int level)
if (level) {
GIC_SET_LEVEL(irq, cm);
- if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
DPRINTF("Set %d pending mask %x\n", irq, target);
GIC_SET_PENDING(irq, target);
}
@@ -168,6 +168,15 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
return new_irq;
}
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
+{
+ if (irq < GIC_INTERNAL) {
+ s->priority1[irq][cpu] = val;
+ } else {
+ s->priority2[(irq) - GIC_INTERNAL] = val;
+ }
+}
+
void gic_complete_irq(GICState *s, int cpu, int irq)
{
int update = 0;
@@ -188,7 +197,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
return; /* No active IRQ. */
/* Mark level triggered interrupts as pending if they are still
raised. */
- if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
@@ -311,7 +320,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
for (i = 0; i < 4; i++) {
if (GIC_TEST_MODEL(irq + i))
res |= (1 << (i * 2));
- if (GIC_TEST_TRIGGER(irq + i))
+ if (GIC_TEST_EDGE_TRIGGER(irq + i))
res |= (2 << (i * 2));
}
} else if (offset < 0xfe0) {
@@ -386,7 +395,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i, mask)
- && !GIC_TEST_TRIGGER(irq + i)) {
+ && !GIC_TEST_EDGE_TRIGGER(irq + i)) {
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
GIC_SET_PENDING(irq + i, mask);
}
@@ -443,11 +452,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- if (irq < GIC_INTERNAL) {
- s->priority1[irq][cpu] = value;
- } else {
- s->priority2[irq - GIC_INTERNAL] = value;
- }
+ gic_set_priority(s, cpu, irq, value);
} else if (offset < 0xc00) {
/* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
* annoying exception of the 11MPCore's GIC.
@@ -478,9 +483,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
GIC_CLEAR_MODEL(irq + i);
}
if (value & (2 << (i * 2))) {
- GIC_SET_TRIGGER(irq + i);
+ GIC_SET_EDGE_TRIGGER(irq + i);
} else {
- GIC_CLEAR_TRIGGER(irq + i);
+ GIC_CLEAR_EDGE_TRIGGER(irq + i);
}
}
} else {
@@ -704,7 +709,6 @@ static void arm_gic_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
ARMGICClass *agc = ARM_GIC_CLASS(klass);
- dc->no_user = 1;
agc->parent_realize = dc->realize;
dc->realize = arm_gic_realize;
}
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index c7658508dd..e4fc65028a 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -51,7 +51,7 @@ static const VMStateDescription vmstate_gic_irq_state = {
VMSTATE_UINT8(active, gic_irq_state),
VMSTATE_UINT8(level, gic_irq_state),
VMSTATE_BOOL(model, gic_irq_state),
- VMSTATE_BOOL(trigger, gic_irq_state),
+ VMSTATE_BOOL(edge_trigger, gic_irq_state),
VMSTATE_END_OF_LIST()
}
};
@@ -126,7 +126,7 @@ static void arm_gic_common_reset(DeviceState *dev)
}
for (i = 0; i < 16; i++) {
GIC_SET_ENABLED(i, ALL_CPU_MASK);
- GIC_SET_TRIGGER(i);
+ GIC_SET_EDGE_TRIGGER(i);
}
if (s->num_cpu == 1) {
/* For uniprocessor GICs all interrupts always target the sole CPU */
@@ -156,7 +156,6 @@ static void arm_gic_common_class_init(ObjectClass *klass, void *data)
dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->vmsd = &vmstate_gic;
- dc->no_user = 1;
}
static const TypeInfo arm_gic_common_type = {
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index f71397542a..59a3da5a6b 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -150,7 +150,6 @@ static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
kgc->parent_reset = dc->reset;
dc->realize = kvm_arm_gic_realize;
dc->reset = kvm_arm_gic_reset;
- dc->no_user = 1;
}
static const TypeInfo kvm_arm_gic_info = {
diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c
index e02da533cb..636262b49f 100644
--- a/hw/intc/etraxfs_pic.c
+++ b/hw/intc/etraxfs_pic.c
@@ -170,6 +170,10 @@ static void etraxfs_pic_class_init(ObjectClass *klass, void *data)
k->init = etraxfs_pic_init;
dc->props = etraxfs_pic_properties;
+ /*
+ * Note: pointer property "interrupt_vector" may remain null, thus
+ * no need for dc->cannot_instantiate_with_device_add_yet = true;
+ */
}
static const TypeInfo etraxfs_pic_info = {
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 3989fd1bd5..8c02d5888c 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -44,9 +44,9 @@
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
-#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = true
-#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = false
-#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true
+#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false
+#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger)
#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \
s->priority1[irq][cpu] : \
s->priority2[(irq) - GIC_INTERNAL])
@@ -61,5 +61,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu);
void gic_complete_irq(GICState *s, int cpu, int irq);
void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c
index 42e00bc4b8..d1813f76b6 100644
--- a/hw/intc/grlib_irqmp.c
+++ b/hw/intc/grlib_irqmp.c
@@ -355,6 +355,8 @@ static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
k->init = grlib_irqmp_init;
dc->reset = grlib_irqmp_reset;
dc->props = grlib_irqmp_properties;
+ /* Reason: pointer properties "set_pil_in", "set_pil_in_opaque" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo grlib_irqmp_info = {
diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c
index 803d037f68..9d293999be 100644
--- a/hw/intc/i8259_common.c
+++ b/hw/intc/i8259_common.c
@@ -135,9 +135,15 @@ static void pic_common_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_pic_common;
- dc->no_user = 1;
dc->props = pic_properties_common;
dc->realize = pic_common_realize;
+ /*
+ * Reason: unlike ordinary ISA devices, the PICs need additional
+ * wiring: its IRQ input lines are set up by board code, and the
+ * wiring of the slave to the master is hard-coded in device model
+ * code.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo pic_common_type = {
diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c
index d866e00297..652dd47a1c 100644
--- a/hw/intc/ioapic.c
+++ b/hw/intc/ioapic.c
@@ -36,6 +36,9 @@
static IOAPICCommonState *ioapics[MAX_IOAPICS];
+/* global variable from ioapic_common.c */
+extern int ioapic_no;
+
static void ioapic_service(IOAPICCommonState *s)
{
uint8_t i;
@@ -225,14 +228,16 @@ static const MemoryRegionOps ioapic_io_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void ioapic_init(IOAPICCommonState *s, int instance_no)
+static void ioapic_realize(DeviceState *dev, Error **errp)
{
+ IOAPICCommonState *s = IOAPIC_COMMON(dev);
+
memory_region_init_io(&s->io_memory, OBJECT(s), &ioapic_io_ops, s,
"ioapic", 0x1000);
- qdev_init_gpio_in(DEVICE(s), ioapic_set_irq, IOAPIC_NUM_PINS);
+ qdev_init_gpio_in(dev, ioapic_set_irq, IOAPIC_NUM_PINS);
- ioapics[instance_no] = s;
+ ioapics[ioapic_no] = s;
}
static void ioapic_class_init(ObjectClass *klass, void *data)
@@ -240,7 +245,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data)
IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- k->init = ioapic_init;
+ k->realize = ioapic_realize;
dc->reset = ioapic_reset_common;
}
diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c
index 6b705c1546..4d3d309b62 100644
--- a/hw/intc/ioapic_common.c
+++ b/hw/intc/ioapic_common.c
@@ -23,6 +23,14 @@
#include "hw/i386/ioapic_internal.h"
#include "hw/sysbus.h"
+/* ioapic_no count start from 0 to MAX_IOAPICS,
+ * remove as static variable from ioapic_common_init.
+ * now as a global variable, let child to increase the counter
+ * then we can drop the 'instance_no' argument
+ * and convert to our QOM's realize function
+ */
+int ioapic_no;
+
void ioapic_reset_common(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
@@ -61,7 +69,6 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
IOAPICCommonClass *info;
- static int ioapic_no;
if (ioapic_no >= MAX_IOAPICS) {
error_setg(errp, "Only %d ioapics allowed", MAX_IOAPICS);
@@ -69,7 +76,7 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp)
}
info = IOAPIC_COMMON_GET_CLASS(s);
- info->init(s, ioapic_no);
+ info->realize(dev, errp);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io_memory);
ioapic_no++;
@@ -98,7 +105,6 @@ static void ioapic_common_class_init(ObjectClass *klass, void *data)
dc->realize = ioapic_common_realize;
dc->vmsd = &vmstate_ioapic_common;
- dc->no_user = 1;
}
static const TypeInfo ioapic_common_type = {
@@ -110,9 +116,9 @@ static const TypeInfo ioapic_common_type = {
.abstract = true,
};
-static void register_types(void)
+static void ioapic_common_register_types(void)
{
type_register_static(&ioapic_common_type);
}
-type_init(register_types)
+type_init(ioapic_common_register_types)
diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c
index 7dd63da802..ad3931c112 100644
--- a/hw/intc/omap_intc.c
+++ b/hw/intc/omap_intc.c
@@ -392,6 +392,8 @@ static void omap_intc_class_init(ObjectClass *klass, void *data)
k->init = omap_intc_init;
dc->reset = omap_inth_reset;
dc->props = omap_intc_properties;
+ /* Reason: pointer property "clk" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo omap_intc_info = {
@@ -637,6 +639,8 @@ static void omap2_intc_class_init(ObjectClass *klass, void *data)
k->init = omap2_intc_init;
dc->reset = omap_inth_reset;
dc->props = omap2_intc_properties;
+ /* Reason: pointer property "iclk", "fclk" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo omap2_intc_info = {
diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c
index 329680da3a..2bf359a76b 100644
--- a/hw/intc/pl190.c
+++ b/hw/intc/pl190.c
@@ -273,7 +273,6 @@ static void pl190_class_init(ObjectClass *klass, void *data)
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = pl190_init;
- dc->no_user = 1;
dc->reset = pl190_reset;
dc->vmsd = &vmstate_pl190;
}
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index a333305d3d..b437563fb9 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -723,7 +723,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nr, server, priority;
if ((nargs != 3) || (nret != 1)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -733,13 +733,13 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
|| (priority > 0xff)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
ics_write_xive(ics, nr, server, priority, priority);
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -751,18 +751,18 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nr;
if ((nargs != 1) || (nret != 3)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
nr = rtas_ld(args, 0);
if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, ics->irqs[nr - ics->offset].server);
rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
}
@@ -776,21 +776,21 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nr;
if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
nr = rtas_ld(args, 0);
if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
ics->irqs[nr - ics->offset].priority);
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -802,14 +802,14 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nr;
if ((nargs != 1) || (nret != 1)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
nr = rtas_ld(args, 0);
if (!ics_valid_irq(ics, nr)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -817,7 +817,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ics->irqs[nr - ics->offset].saved_priority,
ics->irqs[nr - ics->offset].saved_priority);
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
/*
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 9e104eb9a7..55d01008d3 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -197,7 +197,6 @@ static void isabus_bridge_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->fw_name = "isa";
- dc->no_user = 1;
}
static const TypeInfo isabus_bridge_info = {
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 19b2198fa6..51ce12dad6 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -644,14 +644,17 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
dc->reset = ich9_lpc_reset;
k->init = ich9_lpc_initfn;
dc->vmsd = &vmstate_ich9_lpc;
- dc->no_user = 1;
k->config_write = ich9_lpc_config_write;
dc->desc = "ICH9 LPC bridge";
k->vendor_id = PCI_VENDOR_ID_INTEL;
k->device_id = PCI_DEVICE_ID_INTEL_ICH9_8;
k->revision = ICH9_A2_LPC_REVISION;
k->class_id = PCI_CLASS_BRIDGE_ISA;
-
+ /*
+ * Reason: part of ICH9 southbridge, needs to be wired up by
+ * pc_q35_init()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo ich9_lpc_info = {
diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c
index 1a1d4518ce..def6fe3a0f 100644
--- a/hw/isa/piix4.c
+++ b/hw/isa/piix4.c
@@ -113,8 +113,12 @@ static void piix4_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0;
k->class_id = PCI_CLASS_BRIDGE_ISA;
dc->desc = "ISA bridge";
- dc->no_user = 1;
dc->vmsd = &vmstate_piix4;
+ /*
+ * Reason: part of PIIX4 southbridge, needs to be wired up,
+ * e.g. by mips_malta_init()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo piix4_info = {
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index 5fb808630f..e639357db3 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -480,8 +480,12 @@ static void via_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_BRIDGE_ISA;
k->revision = 0x40;
dc->desc = "ISA bridge";
- dc->no_user = 1;
dc->vmsd = &vmstate_via;
+ /*
+ * Reason: part of VIA VT82C686 southbridge, needs to be wired up,
+ * e.g. by mips_fulong2e_init()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo via_info = {
diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
index c65e2aabf1..b2517d87fe 100644
--- a/hw/microblaze/Makefile.objs
+++ b/hw/microblaze/Makefile.objs
@@ -1,4 +1,3 @@
obj-y += petalogix_s3adsp1800_mmu.o
obj-y += petalogix_ml605_mmu.o
obj-y += boot.o
-obj-y += pic_cpu.o
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index 2a7ea5c0f9..48d9e7afa4 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -79,19 +79,19 @@ static int microblaze_load_dtb(hwaddr addr,
}
if (kernel_cmdline) {
- r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
- kernel_cmdline);
+ r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
if (r < 0) {
fprintf(stderr, "couldn't set /chosen/bootargs\n");
}
}
if (initrd_start) {
- qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
- initrd_start);
+ qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ initrd_start);
- qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
- initrd_end);
+ qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ initrd_end);
}
cpu_physical_memory_write(addr, fdt, fdt_size);
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 10970e0f3f..1a87756246 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -39,7 +39,6 @@
#include "hw/ssi.h"
#include "boot.h"
-#include "pic_cpu.h"
#include "hw/stream.h"
@@ -82,20 +81,18 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
Object *ds, *cs;
MicroBlazeCPU *cpu;
SysBusDevice *busdev;
- CPUMBState *env;
DriveInfo *dinfo;
int i;
hwaddr ddr_base = MEMORY_BASEADDR;
MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq irq[32], *cpu_irq;
+ qemu_irq irq[32];
/* init CPUs */
if (cpu_model == NULL) {
cpu_model = "microblaze";
}
cpu = cpu_mb_init(cpu_model);
- env = &cpu->env;
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_ml605.lmb_bram",
@@ -117,8 +114,8 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
2, 0x89, 0x18, 0x0000, 0x0, 0);
- cpu_irq = microblaze_pic_init_cpu(env);
- dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 4);
+ dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu),
+ MB_CPU_IRQ), 4);
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(dev, i);
}
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index ec6489c2d3..f50021506c 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -35,7 +35,6 @@
#include "exec/address-spaces.h"
#include "boot.h"
-#include "pic_cpu.h"
#define LMB_BRAM_SIZE (128 * 1024)
#define FLASH_SIZE (16 * 1024 * 1024)
@@ -63,13 +62,12 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args)
const char *cpu_model = args->cpu_model;
DeviceState *dev;
MicroBlazeCPU *cpu;
- CPUMBState *env;
DriveInfo *dinfo;
int i;
hwaddr ddr_base = MEMORY_BASEADDR;
MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq irq[32], *cpu_irq;
+ qemu_irq irq[32];
MemoryRegion *sysmem = get_system_memory();
/* init CPUs */
@@ -77,7 +75,6 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args)
cpu_model = "microblaze";
}
cpu = cpu_mb_init(cpu_model);
- env = &cpu->env;
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL,
@@ -96,8 +93,8 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args)
FLASH_SIZE >> 16,
1, 0x89, 0x18, 0x0000, 0x0, 1);
- cpu_irq = microblaze_pic_init_cpu(env);
- dev = xilinx_intc_create(INTC_BASEADDR, cpu_irq[0], 0xA);
+ dev = xilinx_intc_create(INTC_BASEADDR, qdev_get_gpio_in(DEVICE(cpu),
+ MB_CPU_IRQ), 0xA);
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(dev, i);
}
diff --git a/hw/microblaze/pic_cpu.c b/hw/microblaze/pic_cpu.c
deleted file mode 100644
index 16902f7880..0000000000
--- a/hw/microblaze/pic_cpu.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * QEMU MicroBlaze CPU interrupt wrapper logic.
- *
- * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
- *
- * 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 "hw/hw.h"
-#include "pic_cpu.h"
-
-#define D(x)
-
-static void microblaze_pic_cpu_handler(void *opaque, int irq, int level)
-{
- MicroBlazeCPU *cpu = opaque;
- CPUState *cs = CPU(cpu);
- int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
-
- if (level) {
- cpu_interrupt(cs, type);
- } else {
- cpu_reset_interrupt(cs, type);
- }
-}
-
-qemu_irq *microblaze_pic_init_cpu(CPUMBState *env)
-{
- return qemu_allocate_irqs(microblaze_pic_cpu_handler, mb_env_get_cpu(env),
- 2);
-}
diff --git a/hw/microblaze/pic_cpu.h b/hw/microblaze/pic_cpu.h
deleted file mode 100644
index 43090a48ef..0000000000
--- a/hw/microblaze/pic_cpu.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef MICROBLAZE_PIC_CPU_H
-#define MICROBLAZE_PIC_CPU_H
-
-#include "qemu-common.h"
-
-qemu_irq *microblaze_pic_init_cpu(CPUMBState *env);
-
-#endif /* MICROBLAZE_PIC_CPU_H */
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
index 3da2e67098..6398514c99 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -1151,12 +1151,18 @@ static int gt64120_pci_init(PCIDevice *d)
static void gt64120_pci_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = gt64120_pci_init;
k->vendor_id = PCI_VENDOR_ID_MARVELL;
k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X;
k->revision = 0x10;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo gt64120_pci_info = {
diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c
index 8e192cdf83..9e220c9a56 100644
--- a/hw/misc/arm_l2x0.c
+++ b/hw/misc/arm_l2x0.c
@@ -179,7 +179,6 @@ static void l2x0_class_init(ObjectClass *klass, void *data)
k->init = l2x0_priv_init;
dc->vmsd = &vmstate_l2x0;
- dc->no_user = 1;
dc->props = l2x0_properties;
dc->reset = l2x0_priv_reset;
}
diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c
index cbf0795c0a..5ec14d1c86 100644
--- a/hw/misc/exynos4210_pmu.c
+++ b/hw/misc/exynos4210_pmu.c
@@ -383,8 +383,7 @@ static const Exynos4210PmuReg exynos4210_pmu_regs[] = {
{"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001},
};
-#define PMU_NUM_OF_REGISTERS \
- (sizeof(exynos4210_pmu_regs) / sizeof(Exynos4210PmuReg))
+#define PMU_NUM_OF_REGISTERS ARRAY_SIZE(exynos4210_pmu_regs)
#define TYPE_EXYNOS4210_PMU "exynos4210.pmu"
#define EXYNOS4210_PMU(obj) \
diff --git a/hw/misc/vmport.c b/hw/misc/vmport.c
index 0b5a5644e4..cd5716a46d 100644
--- a/hw/misc/vmport.c
+++ b/hw/misc/vmport.c
@@ -162,7 +162,8 @@ static void vmport_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = vmport_realizefn;
- dc->no_user = 1;
+ /* Reason: realize sets global port_state */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo vmport_info = {
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 78ebbbca72..6a3c86db48 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -646,6 +646,8 @@ static void etraxfs_eth_class_init(ObjectClass *klass, void *data)
k->init = fs_eth_init;
dc->props = etraxfs_eth_properties;
+ /* Reason: pointer properties "dma_out", "dma_in" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo etraxfs_eth_info = {
diff --git a/hw/net/lance.c b/hw/net/lance.c
index e339f029b7..fe18564e1e 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -161,6 +161,8 @@ static void lance_class_init(ObjectClass *klass, void *data)
dc->reset = lance_reset;
dc->vmsd = &vmstate_lance;
dc->props = lance_properties;
+ /* Reason: pointer property "dma" */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo lance_info = {
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index d312b9c83c..36266083b2 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -1491,16 +1491,14 @@ void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
n->netclient_type = g_strdup(type);
}
-static int virtio_net_device_init(VirtIODevice *vdev)
+static void virtio_net_device_realize(DeviceState *dev, Error **errp)
{
- int i;
-
- DeviceState *qdev = DEVICE(vdev);
- VirtIONet *n = VIRTIO_NET(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIONet *n = VIRTIO_NET(dev);
NetClientState *nc;
+ int i;
- virtio_init(VIRTIO_DEVICE(n), "virtio-net", VIRTIO_ID_NET,
- n->config_size);
+ virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
n->max_queues = MAX(n->nic_conf.queues, 1);
n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
@@ -1540,7 +1538,7 @@ static int virtio_net_device_init(VirtIODevice *vdev)
n->netclient_type, n->netclient_name, n);
} else {
n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf,
- object_get_typename(OBJECT(qdev)), qdev->id, n);
+ object_get_typename(OBJECT(dev)), dev->id, n);
}
peer_test_vnet_hdr(n);
@@ -1567,24 +1565,23 @@ static int virtio_net_device_init(VirtIODevice *vdev)
nc = qemu_get_queue(n->nic);
nc->rxfilter_notify_enabled = 1;
- n->qdev = qdev;
- register_savevm(qdev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
+ n->qdev = dev;
+ register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
- add_boot_device_path(n->nic_conf.bootindex, qdev, "/ethernet-phy@0");
- return 0;
+ add_boot_device_path(n->nic_conf.bootindex, dev, "/ethernet-phy@0");
}
-static int virtio_net_device_exit(DeviceState *qdev)
+static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIONet *n = VIRTIO_NET(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIONet *n = VIRTIO_NET(dev);
int i;
/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);
- unregister_savevm(qdev, "virtio-net", n);
+ unregister_savevm(dev, "virtio-net", n);
if (n->netclient_name) {
g_free(n->netclient_name);
@@ -1615,8 +1612,6 @@ static int virtio_net_device_exit(DeviceState *qdev)
g_free(n->vqs);
qemu_del_nic(n->nic);
virtio_cleanup(vdev);
-
- return 0;
}
static void virtio_net_instance_init(Object *obj)
@@ -1643,10 +1638,11 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_net_device_exit;
+
dc->props = virtio_net_properties;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
- vdc->init = virtio_net_device_init;
+ vdc->realize = virtio_net_device_realize;
+ vdc->unrealize = virtio_net_device_unrealize;
vdc->get_config = virtio_net_get_config;
vdc->set_config = virtio_net_set_config;
vdc->get_features = virtio_net_get_features;
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 3eb7715c22..0bd5eda199 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -980,26 +980,21 @@ static void xilinx_enet_init(Object *obj)
{
XilinxAXIEnet *s = XILINX_AXI_ENET(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- Error *errp = NULL;
object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
- (Object **) &s->tx_data_dev, &errp);
- assert_no_error(errp);
+ (Object **) &s->tx_data_dev, &error_abort);
object_property_add_link(obj, "axistream-control-connected",
TYPE_STREAM_SLAVE,
- (Object **) &s->tx_control_dev, &errp);
- assert_no_error(errp);
+ (Object **) &s->tx_control_dev, &error_abort);
object_initialize(&s->rx_data_dev, sizeof(s->rx_data_dev),
TYPE_XILINX_AXI_ENET_DATA_STREAM);
object_initialize(&s->rx_control_dev, sizeof(s->rx_control_dev),
TYPE_XILINX_AXI_ENET_CONTROL_STREAM);
object_property_add_child(OBJECT(s), "axistream-connected-target",
- (Object *)&s->rx_data_dev, &errp);
- assert_no_error(errp);
+ (Object *)&s->rx_data_dev, &error_abort);
object_property_add_child(OBJECT(s), "axistream-control-connected-target",
- (Object *)&s->rx_control_dev, &errp);
- assert_no_error(errp);
+ (Object *)&s->rx_control_dev, &error_abort);
sysbus_init_irq(sbd, &s->irq);
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index f5dc3ea845..ee96c1681b 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -599,7 +599,6 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = fw_cfg_realize;
- dc->no_user = 1;
dc->reset = fw_cfg_reset;
dc->vmsd = &vmstate_fw_cfg;
dc->props = fw_cfg_properties;
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index beaad682ac..635713e766 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -55,12 +55,12 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
void *membuf;
if ((nargs != 3) || (nret != 2)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
if (!nvram) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
rtas_st(rets, 1, 0);
return;
}
@@ -71,7 +71,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
if (((offset + len) < offset)
|| ((offset + len) > nvram->size)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
rtas_st(rets, 1, 0);
return;
}
@@ -87,7 +87,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
cpu_physical_memory_unmap(membuf, len, 1, len);
- rtas_st(rets, 0, (alen < len) ? -1 : 0);
+ rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS);
rtas_st(rets, 1, (alen < 0) ? 0 : alen);
}
@@ -102,12 +102,12 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
void *membuf;
if ((nargs != 3) || (nret != 2)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
if (!nvram) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -117,7 +117,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
if (((offset + len) < offset)
|| ((offset + len) > nvram->size)) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -132,7 +132,7 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
cpu_physical_memory_unmap(membuf, len, 0, len);
- rtas_st(rets, 0, (alen < len) ? -1 : 0);
+ rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS);
rtas_st(rets, 1, (alen < 0) ? 0 : alen);
}
diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c
index e5e3be829f..a6ca940d55 100644
--- a/hw/pci-bridge/dec.c
+++ b/hw/pci-bridge/dec.c
@@ -116,6 +116,7 @@ static int dec_21154_pci_host_init(PCIDevice *d)
static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = dec_21154_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_DEC;
@@ -123,6 +124,11 @@ static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data)
k->revision = 0x02;
k->class_id = PCI_CLASS_BRIDGE_PCI;
k->is_bridge = 1;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo dec_21154_pci_host_info = {
diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c
index 92f289f8f9..1b399ddbc3 100644
--- a/hw/pci-host/apb.c
+++ b/hw/pci-host/apb.c
@@ -516,11 +516,17 @@ static int pbm_pci_host_init(PCIDevice *d)
static void pbm_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = pbm_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_SUN;
k->device_id = PCI_DEVICE_ID_SUN_SABRE;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo pbm_pci_host_info = {
diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c
index 5086d42c13..902441f10b 100644
--- a/hw/pci-host/bonito.c
+++ b/hw/pci-host/bonito.c
@@ -806,8 +806,12 @@ static void bonito_class_init(ObjectClass *klass, void *data)
k->revision = 0x01;
k->class_id = PCI_CLASS_BRIDGE_HOST;
dc->desc = "Host bridge";
- dc->no_user = 1;
dc->vmsd = &vmstate_bonito;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo bonito_info = {
@@ -819,11 +823,9 @@ static const TypeInfo bonito_info = {
static void bonito_pcihost_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = bonito_pcihost_initfn;
- dc->no_user = 1;
}
static const TypeInfo bonito_pcihost_info = {
diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c
index 4991ec44b0..6c7cfdbeb2 100644
--- a/hw/pci-host/grackle.c
+++ b/hw/pci-host/grackle.c
@@ -82,7 +82,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- phb->bus = pci_register_bus(dev, "pci",
+ phb->bus = pci_register_bus(dev, NULL,
pci_grackle_set_irq,
pci_grackle_map_irq,
pic,
@@ -130,7 +130,11 @@ static void grackle_pci_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
- dc->no_user = 1;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo grackle_pci_info = {
@@ -143,10 +147,8 @@ static const TypeInfo grackle_pci_info = {
static void pci_grackle_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
k->init = pci_grackle_init_device;
- dc->no_user = 1;
}
static const TypeInfo grackle_pci_host_info = {
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index edc974ece3..e89d5c1dfa 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -103,8 +103,6 @@ struct PCII440FXState {
MemoryRegion *system_memory;
MemoryRegion *pci_address_space;
MemoryRegion *ram_memory;
- MemoryRegion pci_hole;
- MemoryRegion pci_hole_64bit;
PAMMemoryRegion pam_regions[13];
MemoryRegion smram_region;
uint8_t smm_enabled;
@@ -313,8 +311,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
ram_addr_t ram_size,
- hwaddr pci_hole_start,
- hwaddr pci_hole_size,
+ ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
MemoryRegion *pci_address_space,
MemoryRegion *ram_memory)
@@ -327,7 +324,6 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
PCII440FXState *f;
unsigned i;
I440FXState *i440fx;
- uint64_t pci_hole64_size;
dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE);
s = PCI_HOST_BRIDGE(dev);
@@ -345,33 +341,12 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
f->ram_memory = ram_memory;
i440fx = I440FX_PCI_HOST_BRIDGE(dev);
- /* Set PCI window size the way seabios has always done it. */
- /* Power of 2 so bios can cover it with a single MTRR */
- if (ram_size <= 0x80000000) {
- i440fx->pci_info.w32.begin = 0x80000000;
- } else if (ram_size <= 0xc0000000) {
- i440fx->pci_info.w32.begin = 0xc0000000;
- } else {
- i440fx->pci_info.w32.begin = 0xe0000000;
- }
+ i440fx->pci_info.w32.begin = below_4g_mem_size;
+
+ /* setup pci memory mapping */
+ pc_pci_as_mapping_init(OBJECT(f), f->system_memory,
+ f->pci_address_space);
- memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
- pci_hole_start, pci_hole_size);
- memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
-
- pci_hole64_size = pci_host_get_hole64_size(i440fx->pci_hole64_size);
-
- pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size,
- pci_hole64_size);
- memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
- f->pci_address_space,
- i440fx->pci_info.w64.begin,
- pci_hole64_size);
- if (pci_hole64_size) {
- memory_region_add_subregion(f->system_memory,
- i440fx->pci_info.w64.begin,
- &f->pci_hole_64bit);
- }
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,
@@ -653,7 +628,6 @@ static void piix3_class_init(ObjectClass *klass, void *data)
dc->desc = "ISA bridge";
dc->vmsd = &vmstate_piix3;
- dc->no_user = 1,
k->no_hotplug = 1;
k->init = piix3_initfn;
k->config_write = piix3_write_config;
@@ -661,6 +635,11 @@ static void piix3_class_init(ObjectClass *klass, void *data)
/* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0;
k->class_id = PCI_CLASS_BRIDGE_ISA;
+ /*
+ * Reason: part of PIIX3 southbridge, needs to be wired up by
+ * pc_piix.c's pc_init1()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo piix3_info = {
@@ -677,7 +656,6 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data)
dc->desc = "ISA bridge";
dc->vmsd = &vmstate_piix3;
- dc->no_user = 1;
k->no_hotplug = 1;
k->init = piix3_initfn;
k->config_write = piix3_write_config_xen;
@@ -685,6 +663,11 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data)
/* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0;
k->class_id = PCI_CLASS_BRIDGE_ISA;
+ /*
+ * Reason: part of PIIX3 southbridge, needs to be wired up by
+ * pc_piix.c's pc_init1()
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
};
static const TypeInfo piix3_xen_info = {
@@ -707,8 +690,12 @@ static void i440fx_class_init(ObjectClass *klass, void *data)
k->revision = 0x02;
k->class_id = PCI_CLASS_BRIDGE_HOST;
dc->desc = "Host bridge";
- dc->no_user = 1;
dc->vmsd = &vmstate_i440fx;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo i440fx_info = {
@@ -745,7 +732,6 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
hc->root_bus_path = i440fx_pcihost_root_bus_path;
dc->realize = i440fx_pcihost_realize;
dc->fw_name = "pci";
- dc->no_user = 1;
dc->props = i440fx_props;
}
diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c
index f00793d819..c80b7cb2f5 100644
--- a/hw/pci-host/ppce500.c
+++ b/hw/pci-host/ppce500.c
@@ -387,6 +387,11 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_MPC8533E;
k->class_id = PCI_CLASS_PROCESSOR_POWERPC;
dc->desc = "Host bridge";
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo e500_host_bridge_info = {
diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c
index 0e71fdbfb1..042dc8f225 100644
--- a/hw/pci-host/prep.c
+++ b/hw/pci-host/prep.c
@@ -198,7 +198,11 @@ static void raven_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_BRIDGE_HOST;
dc->desc = "PReP Host Bridge - Motorola Raven";
dc->vmsd = &vmstate_raven;
- dc->no_user = 1;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo raven_info = {
@@ -215,7 +219,6 @@ static void raven_pcihost_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->realize = raven_pcihost_realizefn;
dc->fw_name = "pci";
- dc->no_user = 1;
}
static const TypeInfo raven_pcihost_info = {
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index c043998e32..4bc2e0118e 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -356,28 +356,11 @@ static int mch_init(PCIDevice *d)
{
int i;
MCHPCIState *mch = MCH_PCI_DEVICE(d);
- uint64_t pci_hole64_size;
-
- /* setup pci memory regions */
- memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
- mch->pci_address_space,
- mch->below_4g_mem_size,
- 0x100000000ULL - mch->below_4g_mem_size);
- memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
- &mch->pci_hole);
-
- pci_hole64_size = pci_host_get_hole64_size(mch->pci_hole64_size);
- pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size,
- pci_hole64_size);
- memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
- mch->pci_address_space,
- mch->pci_info.w64.begin,
- pci_hole64_size);
- if (pci_hole64_size) {
- memory_region_add_subregion(mch->system_memory,
- mch->pci_info.w64.begin,
- &mch->pci_hole_64bit);
- }
+
+ /* setup pci memory mapping */
+ pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory,
+ mch->pci_address_space);
+
/* smram */
cpu_smm_register(&mch_set_smm, mch);
memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region",
@@ -420,6 +403,11 @@ static void mch_class_init(ObjectClass *klass, void *data)
k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH;
k->revision = MCH_HOST_BRIDGE_REVISION_DEFAULT;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo mch_info = {
diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c
index 91530cdd04..e72fe2a70b 100644
--- a/hw/pci-host/uninorth.c
+++ b/hw/pci-host/uninorth.c
@@ -234,7 +234,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- h->bus = pci_register_bus(dev, "pci",
+ h->bus = pci_register_bus(dev, NULL,
pci_unin_set_irq, pci_unin_map_irq,
pic,
&d->pci_mmio,
@@ -300,7 +300,7 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic,
memory_region_add_subregion(address_space_mem, 0x80000000ULL,
&d->pci_hole);
- h->bus = pci_register_bus(dev, "pci",
+ h->bus = pci_register_bus(dev, NULL,
pci_unin_set_irq, pci_unin_map_irq,
pic,
&d->pci_mmio,
@@ -351,12 +351,18 @@ static int unin_internal_pci_host_init(PCIDevice *d)
static void unin_main_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = unin_main_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo unin_main_pci_host_info = {
@@ -369,12 +375,18 @@ static const TypeInfo unin_main_pci_host_info = {
static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = u3_agp_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo u3_agp_pci_host_info = {
@@ -387,12 +399,18 @@ static const TypeInfo u3_agp_pci_host_info = {
static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = unin_agp_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo unin_agp_pci_host_info = {
@@ -405,12 +423,18 @@ static const TypeInfo unin_agp_pci_host_info = {
static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = unin_internal_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI;
k->revision = 0x00;
k->class_id = PCI_CLASS_BRIDGE_HOST;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo unin_internal_pci_host_info = {
diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c
index 6b28929d26..71ff0de303 100644
--- a/hw/pci-host/versatile.c
+++ b/hw/pci-host/versatile.c
@@ -467,11 +467,17 @@ static int versatile_pci_host_init(PCIDevice *d)
static void versatile_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = versatile_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_XILINX;
k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30;
k->class_id = PCI_CLASS_PROCESSOR_CO;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo versatile_pci_host_info = {
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 49eca955aa..aa2a395499 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -46,7 +46,7 @@
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *pcibus_get_dev_path(DeviceState *dev);
static char *pcibus_get_fw_dev_path(DeviceState *dev);
-static int pcibus_reset(BusState *qbus);
+static void pcibus_reset(BusState *qbus);
static void pci_bus_finalize(Object *obj);
static Property pci_props[] = {
@@ -167,16 +167,10 @@ void pci_device_deassert_intx(PCIDevice *dev)
}
}
-/*
- * This function is called on #RST and FLR.
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
- */
-void pci_device_reset(PCIDevice *dev)
+static void pci_do_device_reset(PCIDevice *dev)
{
int r;
- qdev_reset_all(&dev->qdev);
-
dev->irq_state = 0;
pci_update_irq_status(dev);
pci_device_deassert_intx(dev);
@@ -209,30 +203,34 @@ void pci_device_reset(PCIDevice *dev)
}
/*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+ qdev_reset_all(&dev->qdev);
+ pci_do_device_reset(dev);
+}
+
+/*
* Trigger pci bus reset under a given bus.
- * To be called on RST# assert.
+ * Called via qbus_reset_all on RST# assert, after the devices
+ * have been reset qdev_reset_all-ed already.
*/
-void pci_bus_reset(PCIBus *bus)
+static void pcibus_reset(BusState *qbus)
{
+ PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
int i;
- for (i = 0; i < bus->nirq; i++) {
- bus->irq_count[i] = 0;
- }
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
if (bus->devices[i]) {
- pci_device_reset(bus->devices[i]);
+ pci_do_device_reset(bus->devices[i]);
}
}
-}
-
-static int pcibus_reset(BusState *qbus)
-{
- pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
- /* topology traverse is done by pci_bus_reset().
- Tell qbus/qdev walker not to traverse the tree */
- return 1;
+ for (i = 0; i < bus->nirq; i++) {
+ assert(bus->irq_count[i] == 0);
+ }
}
static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
@@ -1330,7 +1328,7 @@ static const pci_class_desc pci_class_descriptions[] =
{ 0x0601, "ISA bridge", "isa"},
{ 0x0602, "EISA bridge", "eisa"},
{ 0x0603, "MC bridge", "mca"},
- { 0x0604, "PCI bridge", "pci"},
+ { 0x0604, "PCI bridge", "pci-bridge"},
{ 0x0605, "PCMCIA bridge", "pcmcia"},
{ 0x0606, "NUBUS bridge", "nubus"},
{ 0x0607, "CARDBUS bridge", "cardbus"},
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index 290ababb8b..4becdc14b8 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -268,7 +268,7 @@ void pci_bridge_write_config(PCIDevice *d,
newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) {
/* Trigger hot reset on 0->1 transition. */
- pci_bus_reset(&s->sec_bus);
+ qbus_reset_all(&s->sec_bus.qbus);
}
}
@@ -372,7 +372,7 @@ int pci_bridge_initfn(PCIDevice *dev, const char *typename)
sec_bus->parent_dev = dev;
sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn;
sec_bus->address_space_mem = &br->address_space_mem;
- memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", INT64_MAX);
+ memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX);
sec_bus->address_space_io = &br->address_space_io;
memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 65536);
br->windows = pci_bridge_region_init(br);
@@ -391,7 +391,7 @@ void pci_bridge_exitfn(PCIDevice *pci_dev)
pci_bridge_region_cleanup(s, s->windows);
memory_region_destroy(&s->address_space_mem);
memory_region_destroy(&s->address_space_io);
- /* qbus_free() is called automatically during device deletion */
+ /* object_unparent() is called automatically during device deletion */
}
/*
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index cfdd84b969..b37ce9d633 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -108,18 +108,18 @@ static void dt_serial_create(void *fdt, unsigned long long offset,
char ser[128];
snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset);
- qemu_devtree_add_subnode(fdt, ser);
- qemu_devtree_setprop_string(fdt, ser, "device_type", "serial");
- qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550");
- qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100);
- qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx);
- qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0);
- qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2);
- qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic);
- qemu_devtree_setprop_string(fdt, "/aliases", alias, ser);
+ qemu_fdt_add_subnode(fdt, ser);
+ qemu_fdt_setprop_string(fdt, ser, "device_type", "serial");
+ qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550");
+ qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100);
+ qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx);
+ qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", 0);
+ qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2);
+ qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic);
+ qemu_fdt_setprop_string(fdt, "/aliases", alias, ser);
if (defcon) {
- qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser);
+ qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser);
}
}
@@ -183,30 +183,30 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
}
/* Manipulate device tree in memory. */
- qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2);
- qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 2);
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 2);
- qemu_devtree_add_subnode(fdt, "/memory");
- qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory");
- qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
- sizeof(mem_reg_property));
+ qemu_fdt_add_subnode(fdt, "/memory");
+ qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory");
+ qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property,
+ sizeof(mem_reg_property));
- qemu_devtree_add_subnode(fdt, "/chosen");
+ qemu_fdt_add_subnode(fdt, "/chosen");
if (initrd_size) {
- ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
- initrd_base);
+ ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ initrd_base);
if (ret < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
}
- ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
- (initrd_base + initrd_size));
+ ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ (initrd_base + initrd_size));
if (ret < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
}
}
- ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
args->kernel_cmdline);
if (ret < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
@@ -217,22 +217,22 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
tb_freq = kvmppc_get_tbfreq();
/* indicate KVM hypercall interface */
- qemu_devtree_add_subnode(fdt, "/hypervisor");
- qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible",
- "linux,kvm");
+ qemu_fdt_add_subnode(fdt, "/hypervisor");
+ qemu_fdt_setprop_string(fdt, "/hypervisor", "compatible",
+ "linux,kvm");
kvmppc_get_hypercall(env, hypercall, sizeof(hypercall));
- qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions",
- hypercall, sizeof(hypercall));
+ qemu_fdt_setprop(fdt, "/hypervisor", "hcall-instructions",
+ hypercall, sizeof(hypercall));
/* if KVM supports the idle hcall, set property indicating this */
if (kvmppc_get_hasidle(env)) {
- qemu_devtree_setprop(fdt, "/hypervisor", "has-idle", NULL, 0);
+ qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0);
}
}
/* Create CPU nodes */
- qemu_devtree_add_subnode(fdt, "/cpus");
- qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1);
- qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0);
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0);
/* We need to generate the cpu nodes in reverse order, so Linux can pick
the first node as boot node and be happy */
@@ -249,55 +249,56 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x",
cpu->cpu_index);
- qemu_devtree_add_subnode(fdt, cpu_name);
- qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq);
- qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq);
- qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu");
- qemu_devtree_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index);
- qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size",
- env->dcache_line_size);
- qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size",
- env->icache_line_size);
- qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000);
- qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000);
- qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0);
+ qemu_fdt_add_subnode(fdt, cpu_name);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq);
+ qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
+ qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size",
+ env->dcache_line_size);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size",
+ env->icache_line_size);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000);
+ qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0);
if (cpu->cpu_index) {
- qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled");
- qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table");
- qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr",
- cpu_release_addr);
+ qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled");
+ qemu_fdt_setprop_string(fdt, cpu_name, "enable-method",
+ "spin-table");
+ qemu_fdt_setprop_u64(fdt, cpu_name, "cpu-release-addr",
+ cpu_release_addr);
} else {
- qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay");
+ qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
}
}
- qemu_devtree_add_subnode(fdt, "/aliases");
+ qemu_fdt_add_subnode(fdt, "/aliases");
/* XXX These should go into their respective devices' code */
snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE);
- qemu_devtree_add_subnode(fdt, soc);
- qemu_devtree_setprop_string(fdt, soc, "device_type", "soc");
- qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb,
- sizeof(compatible_sb));
- qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1);
- qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1);
- qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0,
- MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE,
- MPC8544_CCSRBAR_SIZE);
+ qemu_fdt_add_subnode(fdt, soc);
+ qemu_fdt_setprop_string(fdt, soc, "device_type", "soc");
+ qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb,
+ sizeof(compatible_sb));
+ qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1);
+ qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0,
+ MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE,
+ MPC8544_CCSRBAR_SIZE);
/* XXX should contain a reasonable value */
- qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0);
+ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0);
snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET);
- qemu_devtree_add_subnode(fdt, mpic);
- qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic");
- qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic");
- qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET,
- 0x40000);
- qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0);
- qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 2);
- mpic_ph = qemu_devtree_alloc_phandle(fdt);
- qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph);
- qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph);
- qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0);
+ qemu_fdt_add_subnode(fdt, mpic);
+ qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic");
+ qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic");
+ qemu_fdt_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET,
+ 0x40000);
+ qemu_fdt_setprop_cell(fdt, mpic, "#address-cells", 0);
+ qemu_fdt_setprop_cell(fdt, mpic, "#interrupt-cells", 2);
+ mpic_ph = qemu_fdt_alloc_phandle(fdt);
+ qemu_fdt_setprop_cell(fdt, mpic, "phandle", mpic_ph);
+ qemu_fdt_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph);
+ qemu_fdt_setprop(fdt, mpic, "interrupt-controller", NULL, 0);
/*
* We have to generate ser1 first, because Linux takes the first
@@ -311,19 +312,19 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc,
MPC8544_UTIL_OFFSET);
- qemu_devtree_add_subnode(fdt, gutil);
- qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts");
- qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000);
- qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0);
+ qemu_fdt_add_subnode(fdt, gutil);
+ qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts");
+ qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000);
+ qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0);
snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET);
- qemu_devtree_add_subnode(fdt, msi);
- qemu_devtree_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi");
- qemu_devtree_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200);
- msi_ph = qemu_devtree_alloc_phandle(fdt);
- qemu_devtree_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100);
- qemu_devtree_setprop_phandle(fdt, msi, "interrupt-parent", mpic);
- qemu_devtree_setprop_cells(fdt, msi, "interrupts",
+ qemu_fdt_add_subnode(fdt, msi);
+ qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi");
+ qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200);
+ msi_ph = qemu_fdt_alloc_phandle(fdt);
+ qemu_fdt_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100);
+ qemu_fdt_setprop_phandle(fdt, msi, "interrupt-parent", mpic);
+ qemu_fdt_setprop_cells(fdt, msi, "interrupts",
0xe0, 0x0,
0xe1, 0x0,
0xe2, 0x0,
@@ -332,46 +333,46 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
0xe5, 0x0,
0xe6, 0x0,
0xe7, 0x0);
- qemu_devtree_setprop_cell(fdt, msi, "phandle", msi_ph);
- qemu_devtree_setprop_cell(fdt, msi, "linux,phandle", msi_ph);
+ qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph);
+ qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph);
snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE);
- qemu_devtree_add_subnode(fdt, pci);
- qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0);
- qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci");
- qemu_devtree_setprop_string(fdt, pci, "device_type", "pci");
- qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0,
- 0x0, 0x7);
- pci_map = pci_map_create(fdt, qemu_devtree_get_phandle(fdt, mpic),
+ qemu_fdt_add_subnode(fdt, pci);
+ qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0);
+ qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci");
+ qemu_fdt_setprop_string(fdt, pci, "device_type", "pci");
+ qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0,
+ 0x0, 0x7);
+ pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic),
params->pci_first_slot, params->pci_nr_slots,
&len);
- qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, len);
- qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic);
- qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2);
- qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255);
+ qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len);
+ qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic);
+ qemu_fdt_setprop_cells(fdt, pci, "interrupts", 24, 2);
+ qemu_fdt_setprop_cells(fdt, pci, "bus-range", 0, 255);
for (i = 0; i < 14; i++) {
pci_ranges[i] = cpu_to_be32(pci_ranges[i]);
}
- qemu_devtree_setprop_cell(fdt, pci, "fsl,msi", msi_ph);
- qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges));
- qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32,
- MPC8544_PCI_REGS_BASE, 0, 0x1000);
- qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666);
- qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1);
- qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2);
- qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3);
- qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci);
+ qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph);
+ qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges));
+ qemu_fdt_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32,
+ MPC8544_PCI_REGS_BASE, 0, 0x1000);
+ qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666);
+ qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1);
+ qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2);
+ qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3);
+ qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci);
params->fixup_devtree(params, fdt);
if (toplevel_compat) {
- qemu_devtree_setprop(fdt, "/", "compatible", toplevel_compat,
- strlen(toplevel_compat) + 1);
+ qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat,
+ strlen(toplevel_compat) + 1);
}
done:
if (!dry_run) {
- qemu_devtree_dumpdtb(fdt, fdt_size);
+ qemu_fdt_dumpdtb(fdt, fdt_size);
cpu_physical_memory_write(addr, fdt, fdt_size);
}
ret = fdt_size;
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index 2e964b2474..7d5357e83b 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -23,9 +23,9 @@ static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt)
const char model[] = "QEMU ppce500";
const char compatible[] = "fsl,qemu-e500";
- qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model));
- qemu_devtree_setprop(fdt, "/", "compatible", compatible,
- sizeof(compatible));
+ qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model));
+ qemu_fdt_setprop(fdt, "/", "compatible", compatible,
+ sizeof(compatible));
}
static void e500plat_init(QEMUMachineInitArgs *args)
diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c
index edcc0be5f7..292c70953b 100644
--- a/hw/ppc/mpc8544ds.c
+++ b/hw/ppc/mpc8544ds.c
@@ -21,9 +21,9 @@ static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt)
const char model[] = "MPC8544DS";
const char compatible[] = "MPC8544DS\0MPC85xxDS";
- qemu_devtree_setprop(fdt, "/", "model", model, sizeof(model));
- qemu_devtree_setprop(fdt, "/", "compatible", compatible,
- sizeof(compatible));
+ qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model));
+ qemu_fdt_setprop(fdt, "/", "compatible", compatible,
+ sizeof(compatible));
}
static void mpc8544ds_init(QEMUMachineInitArgs *args)
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 67597dfb88..ec15bab0b5 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -77,23 +77,23 @@ static int bamboo_load_device_tree(hwaddr addr,
/* Manipulate device tree in memory. */
- ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
- sizeof(mem_reg_property));
+ ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property,
+ sizeof(mem_reg_property));
if (ret < 0)
fprintf(stderr, "couldn't set /memory/reg\n");
- ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
- initrd_base);
+ ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ initrd_base);
if (ret < 0)
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
- ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
- (initrd_base + initrd_size));
+ ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ (initrd_base + initrd_size));
if (ret < 0)
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
- ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
- kernel_cmdline);
+ ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
if (ret < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
@@ -105,10 +105,10 @@ static int bamboo_load_device_tree(hwaddr addr,
clock_freq = kvmppc_get_clockfreq();
}
- qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
- clock_freq);
- qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
- tb_freq);
+ qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
+ clock_freq);
+ qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
+ tb_freq);
rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
g_free(fdt);
diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c
index d2d6f65e6c..4cb78518a3 100644
--- a/hw/ppc/ppc4xx_pci.c
+++ b/hw/ppc/ppc4xx_pci.c
@@ -380,6 +380,11 @@ static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_IBM;
k->device_id = PCI_DEVICE_ID_IBM_440GX;
k->class_id = PCI_CLASS_BRIDGE_OTHER;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo ppc4xx_host_bridge_info = {
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 7e53a5f977..93d02c1e50 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -526,14 +526,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
cpu_to_be32(0x0), cpu_to_be32(0x0),
cpu_to_be32(0x0)};
char mem_name[32];
- hwaddr node0_size, mem_start;
+ hwaddr node0_size, mem_start, node_size;
uint64_t mem_reg_property[2];
int i, off;
/* memory node(s) */
- node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
- if (spapr->rma_size > node0_size) {
- spapr->rma_size = node0_size;
+ if (nb_numa_nodes > 1 && node_mem[0] < ram_size) {
+ node0_size = node_mem[0];
+ } else {
+ node0_size = ram_size;
}
/* RMA */
@@ -566,7 +567,15 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
mem_start = node0_size;
for (i = 1; i < nb_numa_nodes; i++) {
mem_reg_property[0] = cpu_to_be64(mem_start);
- mem_reg_property[1] = cpu_to_be64(node_mem[i]);
+ if (mem_start >= ram_size) {
+ node_size = 0;
+ } else {
+ node_size = node_mem[i];
+ if (node_size > ram_size - mem_start) {
+ node_size = ram_size - mem_start;
+ }
+ }
+ mem_reg_property[1] = cpu_to_be64(node_size);
associativity[3] = associativity[4] = cpu_to_be32(i);
sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start);
off = fdt_add_subnode(fdt, 0, mem_name);
@@ -576,7 +585,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
sizeof(mem_reg_property))));
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
sizeof(associativity))));
- mem_start += node_mem[i];
+ mem_start += node_size;
}
return 0;
@@ -688,7 +697,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr)
/* Update the RMA size if necessary */
if (spapr->vrma_adjust) {
- spapr->rma_size = kvmppc_rma_size(ram_size, spapr->htab_shift);
+ hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
+ spapr->rma_size = kvmppc_rma_size(node0_size, spapr->htab_shift);
}
}
@@ -739,18 +749,10 @@ static void spapr_cpu_reset(void *opaque)
static void spapr_create_nvram(sPAPREnvironment *spapr)
{
DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram");
- const char *drivename = qemu_opt_get(qemu_get_machine_opts(), "nvram");
-
- if (drivename) {
- BlockDriverState *bs;
+ DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
- bs = bdrv_find(drivename);
- if (!bs) {
- fprintf(stderr, "No such block device \"%s\" for nvram\n",
- drivename);
- exit(1);
- }
- qdev_prop_set_drive_nofail(dev, "drive", bs);
+ if (dinfo) {
+ qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
}
qdev_init_nofail(dev);
@@ -1113,6 +1115,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
hwaddr rma_alloc_size;
+ hwaddr node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
uint32_t initrd_base = 0;
long kernel_size = 0, initrd_size = 0;
long load_limit, rtas_limit, fw_size;
@@ -1134,10 +1137,10 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
exit(1);
}
- if (rma_alloc_size && (rma_alloc_size < ram_size)) {
+ if (rma_alloc_size && (rma_alloc_size < node0_size)) {
spapr->rma_size = rma_alloc_size;
} else {
- spapr->rma_size = ram_size;
+ spapr->rma_size = node0_size;
/* With KVM, we don't actually know whether KVM supports an
* unbounded RMA (PR KVM) or is limited by the hash table size
@@ -1154,6 +1157,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
}
}
+ if (spapr->rma_size > node0_size) {
+ fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n",
+ spapr->rma_size);
+ exit(1);
+ }
+
/* We place the device tree and RTAS just below either the top of the RMA,
* or just below 2GB, whichever is lowere, so that it can be
* processed with 32-bit real mode code if necessary */
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index a69390e54e..16fa49e886 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -286,7 +286,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint64_t xinfo;
if ((nargs < 6) || (nargs > 7) || nret != 1) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -306,9 +306,9 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
cpu_physical_memory_write(buf, pending_epow, len);
g_free(pending_epow);
pending_epow = NULL;
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
} else {
- rtas_st(rets, 0, 1);
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
}
}
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index edb4cb0413..ec00300884 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -90,7 +90,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
if ((size != 1) && (size != 2) && (size != 4)) {
/* access must be 1, 2 or 4 bytes */
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -100,14 +100,14 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
/* Access must be to a valid device, within bounds and
* naturally aligned */
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
val = pci_host_config_read_common(pci_dev, addr,
pci_config_size(pci_dev), size);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, val);
}
@@ -120,7 +120,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t size, addr;
if ((nargs != 4) || (nret != 2)) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -139,7 +139,7 @@ static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t size, addr;
if ((nargs != 2) || (nret != 2)) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -157,7 +157,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
if ((size != 1) && (size != 2) && (size != 4)) {
/* access must be 1, 2 or 4 bytes */
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -167,14 +167,14 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
/* Access must be to a valid device, within bounds and
* naturally aligned */
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev),
val, size);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -186,7 +186,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t val, size, addr;
if ((nargs != 5) || (nret != 1)) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -206,7 +206,7 @@ static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t val, size, addr;
if ((nargs != 3) || (nret != 1)) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -293,7 +293,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
break;
default:
fprintf(stderr, "rtas_ibm_change_msi(%u) is not implemented\n", func);
- rtas_st(rets, 0, -3); /* Parameter error */
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -303,7 +303,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
pdev = find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
- rtas_st(rets, 0, -3); /* Parameter error */
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -312,11 +312,11 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ndev = spapr_msicfg_find(phb, config_addr, false);
if (ndev < 0) {
trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr);
- rtas_st(rets, 0, -1); /* Hardware error */
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
trace_spapr_pci_msi("Released MSIs", ndev, config_addr);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, 0);
return;
}
@@ -327,7 +327,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ndev = spapr_msicfg_find(phb, config_addr, true);
if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) {
fprintf(stderr, "No free entry for a new MSI device\n");
- rtas_st(rets, 0, -1); /* Hardware error */
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
trace_spapr_pci_msi("Configuring MSI", ndev, config_addr);
@@ -336,7 +336,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) {
/* Unexpected behaviour */
fprintf(stderr, "Cannot reuse MSI config for device#%d", ndev);
- rtas_st(rets, 0, -1); /* Hardware error */
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -346,7 +346,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ret_intr_type == RTAS_TYPE_MSI);
if (irq < 0) {
fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev);
- rtas_st(rets, 0, -1); /* Hardware error */
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
phb->msi_table[ndev].irq = irq;
@@ -358,7 +358,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
spapr_msi_setmsg(pdev, spapr->msi_win_addr, ret_intr_type == RTAS_TYPE_MSIX,
phb->msi_table[ndev].irq, req_num);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, req_num);
rtas_st(rets, 2, ++seq_num);
rtas_st(rets, 3, ret_intr_type);
@@ -383,7 +383,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
/* Fins sPAPRPHBState */
phb = find_phb(spapr, buid);
if (!phb) {
- rtas_st(rets, 0, -3); /* Parameter error */
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -391,7 +391,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
ndev = spapr_msicfg_find(phb, config_addr, false);
if (ndev < 0) {
trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr);
- rtas_st(rets, 0, -1); /* Hardware error */
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -399,7 +399,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num,
intr_src_num);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, intr_src_num);
rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */
}
@@ -555,7 +555,7 @@ static int spapr_phb_init(SysBusDevice *s)
/* Initialize memory regions */
sprintf(namebuf, "%s.mmio", sphb->dtbusname);
- memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, INT64_MAX);
+ memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, UINT64_MAX);
sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname);
memory_region_init_alias(&sphb->memwindow, OBJECT(sphb),
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index eb542f218a..1cb276de05 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -47,10 +47,10 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
VIOsPAPRDevice *sdev = vty_lookup(spapr, 0);
if (!sdev) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
} else {
vty_putchars(sdev, &c, sizeof(c));
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
}
@@ -62,13 +62,13 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
struct tm tm;
if (nret != 8) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
qemu_get_timedate(&tm, spapr->rtc_offset);
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, tm.tm_year + 1900);
rtas_st(rets, 2, tm.tm_mon + 1);
rtas_st(rets, 3, tm.tm_mday);
@@ -96,7 +96,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtc_change_mon_event(&tm);
spapr->rtc_offset = qemu_timedate_diff(&tm);
- rtas_st(rets, 0, 0); /* Success */
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -104,11 +104,11 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nret, target_ulong rets)
{
if (nargs != 2 || nret != 1) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
qemu_system_shutdown_request();
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -117,11 +117,11 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t nret, target_ulong rets)
{
if (nargs != 0 || nret != 1) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
qemu_system_reset_request();
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
@@ -134,7 +134,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
CPUState *cpu;
if (nargs != 1 || nret != 2) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -147,12 +147,12 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
rtas_st(rets, 1, 2);
}
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
return;
}
/* Didn't find a matching cpu */
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
}
static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
@@ -164,7 +164,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
CPUState *cs;
if (nargs != 3 || nret != 1) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -178,7 +178,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
CPUPPCState *env = &cpu->env;
if (!cs->halted) {
- rtas_st(rets, 0, -1);
+ rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
@@ -194,12 +194,12 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
qemu_cpu_kick(cs);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
return;
}
/* Didn't find a matching cpu */
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
}
static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -224,6 +224,49 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr,
env->msr = 0;
}
+#define DIAGNOSTICS_RUN_MODE 42
+
+static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ target_ulong parameter = rtas_ld(args, 0);
+ target_ulong buffer = rtas_ld(args, 1);
+ target_ulong length = rtas_ld(args, 2);
+ target_ulong ret = RTAS_OUT_NOT_SUPPORTED;
+
+ switch (parameter) {
+ case DIAGNOSTICS_RUN_MODE:
+ if (length == 1) {
+ rtas_st(buffer, 0, 0);
+ ret = RTAS_OUT_SUCCESS;
+ }
+ break;
+ }
+
+ rtas_st(rets, 0, ret);
+}
+
+static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ target_ulong parameter = rtas_ld(args, 0);
+ target_ulong ret = RTAS_OUT_NOT_SUPPORTED;
+
+ switch (parameter) {
+ case DIAGNOSTICS_RUN_MODE:
+ ret = RTAS_OUT_NOT_AUTHORIZED;
+ break;
+ }
+
+ rtas_st(rets, 0, ret);
+}
+
static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
@@ -255,7 +298,7 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
hcall_dprintf("Unknown RTAS token 0x%x\n", token);
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return H_PARAMETER;
}
@@ -291,24 +334,24 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
return ret;
}
- ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
- rtas_addr);
+ ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base",
+ rtas_addr);
if (ret < 0) {
fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
fdt_strerror(ret));
return ret;
}
- ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
- rtas_addr);
+ ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
+ rtas_addr);
if (ret < 0) {
fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
fdt_strerror(ret));
return ret;
}
- ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
- rtas_size);
+ ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size",
+ rtas_size);
if (ret < 0) {
fprintf(stderr, "Couldn't add rtas-size property: %s\n",
fdt_strerror(ret));
@@ -322,8 +365,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
continue;
}
- ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
- i + TOKEN_BASE);
+ ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name,
+ i + TOKEN_BASE);
if (ret < 0) {
fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
call->name, fdt_strerror(ret));
@@ -345,6 +388,10 @@ static void core_rtas_register_types(void)
rtas_query_cpu_stopped_state);
spapr_rtas_register("start-cpu", rtas_start_cpu);
spapr_rtas_register("stop-self", rtas_stop_self);
+ spapr_rtas_register("ibm,get-system-parameter",
+ rtas_ibm_get_system_parameter);
+ spapr_rtas_register("ibm,set-system-parameter",
+ rtas_ibm_set_system_parameter);
}
type_init(core_rtas_register_types)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index a6a0a5113c..4e33f462d9 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -331,25 +331,25 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t unit, enable;
if (nargs != 2) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
unit = rtas_ld(args, 0);
enable = rtas_ld(args, 1);
dev = spapr_vio_find_by_reg(bus, unit);
if (!dev) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
if (!dev->tcet) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
spapr_tce_set_bypass(dev->tcet, !!enable);
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -362,7 +362,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
VIOsPAPRDevice *dev = NULL;
if (nargs != 0) {
- rtas_st(rets, 0, -3);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
@@ -371,7 +371,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
spapr_vio_quiesce_one(dev);
}
- rtas_st(rets, 0, 0);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
@@ -528,11 +528,9 @@ static int spapr_vio_bridge_init(SysBusDevice *dev)
static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = spapr_vio_bridge_init;
- dc->no_user = 1;
}
static const TypeInfo spapr_vio_bridge_info = {
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index fcfa678344..bdb057e36c 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -166,7 +166,7 @@ static int xilinx_load_device_tree(hwaddr addr,
if (!fdt) {
return 0;
}
- r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
+ r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
if (r < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
cpu_physical_memory_write(addr, fdt, fdt_size);
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 65d39da314..1a6397b88e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -182,7 +182,6 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
k->init = s390_ipl_init;
dc->props = s390_ipl_properties;
dc->reset = s390_ipl_reset;
- dc->no_user = 1;
}
static const TypeInfo s390_ipl_info = {
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 6a831114da..46c5ff1898 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -676,11 +676,9 @@ static int s390_virtio_bridge_init(SysBusDevice *dev)
static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = s390_virtio_bridge_init;
- dc->no_user = 1;
}
static const TypeInfo s390_virtio_bridge_info = {
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index f93a81c7cd..bc8871249d 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -30,13 +30,10 @@
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
VirtioCcwDevice *dev);
-static int virtual_css_bus_reset(BusState *qbus)
+static void virtual_css_bus_reset(BusState *qbus)
{
/* This should actually be modelled via the generic css */
css_reset();
-
- /* we dont traverse ourself, return 0 */
- return 0;
}
@@ -57,9 +54,10 @@ static const TypeInfo virtual_css_bus_info = {
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
{
VirtIODevice *vdev = NULL;
+ VirtioCcwDevice *dev = sch->driver_data;
- if (sch->driver_data) {
- vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev;
+ if (dev) {
+ vdev = virtio_bus_get_device(&dev->bus);
}
return vdev;
}
@@ -67,7 +65,8 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
bool assign, bool set_handler)
{
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
int r = 0;
SubchDev *sch = dev->sch;
@@ -97,6 +96,7 @@ static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
{
+ VirtIODevice *vdev;
int n, r;
if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
@@ -104,8 +104,9 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
dev->ioeventfd_started) {
return;
}
+ vdev = virtio_bus_get_device(&dev->bus);
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
@@ -118,7 +119,7 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
assign_error:
while (--n >= 0) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
@@ -132,13 +133,15 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
{
+ VirtIODevice *vdev;
int n, r;
if (!dev->ioeventfd_started) {
return;
}
+ vdev = virtio_bus_get_device(&dev->bus);
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(dev->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
@@ -189,7 +192,7 @@ typedef struct VirtioFeatDesc {
static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
uint16_t index, uint16_t num)
{
- VirtioCcwDevice *dev = sch->driver_data;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
if (index > VIRTIO_PCI_QUEUE_MAX) {
return -EINVAL;
@@ -200,23 +203,23 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
return -EINVAL;
}
- if (!dev) {
+ if (!vdev) {
return -EINVAL;
}
- virtio_queue_set_addr(dev->vdev, index, addr);
+ virtio_queue_set_addr(vdev, index, addr);
if (!addr) {
- virtio_queue_set_vector(dev->vdev, index, 0);
+ virtio_queue_set_vector(vdev, index, 0);
} else {
/* Fail if we don't have a big enough queue. */
/* TODO: Add interface to handle vring.num changing */
- if (virtio_queue_get_num(dev->vdev, index) > num) {
+ if (virtio_queue_get_num(vdev, index) > num) {
return -EINVAL;
}
- virtio_queue_set_vector(dev->vdev, index, index);
+ virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
- dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
+ vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
return 0;
}
@@ -230,6 +233,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
hwaddr indicators;
VqConfigBlock vq_config;
VirtioCcwDevice *dev = sch->driver_data;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
bool check_len;
int len;
hwaddr hw_len;
@@ -272,7 +276,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
break;
case CCW_CMD_VDEV_RESET:
virtio_ccw_stop_ioeventfd(dev);
- virtio_reset(dev->vdev);
+ virtio_reset(vdev);
ret = 0;
break;
case CCW_CMD_READ_FEAT:
@@ -319,7 +323,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
features.features = ldl_le_phys(ccw.cda);
if (features.index < ARRAY_SIZE(dev->host_features)) {
virtio_bus_set_vdev_features(&dev->bus, features.features);
- dev->vdev->guest_features = features.features;
+ vdev->guest_features = features.features;
} else {
/*
* If the guest supports more feature bits, assert that it
@@ -337,30 +341,30 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
break;
case CCW_CMD_READ_CONF:
if (check_len) {
- if (ccw.count > dev->vdev->config_len) {
+ if (ccw.count > vdev->config_len) {
ret = -EINVAL;
break;
}
}
- len = MIN(ccw.count, dev->vdev->config_len);
+ len = MIN(ccw.count, vdev->config_len);
if (!ccw.cda) {
ret = -EFAULT;
} else {
- virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config);
+ virtio_bus_get_vdev_config(&dev->bus, vdev->config);
/* XXX config space endianness */
- cpu_physical_memory_write(ccw.cda, dev->vdev->config, len);
+ cpu_physical_memory_write(ccw.cda, vdev->config, len);
sch->curr_status.scsw.count = ccw.count - len;
ret = 0;
}
break;
case CCW_CMD_WRITE_CONF:
if (check_len) {
- if (ccw.count > dev->vdev->config_len) {
+ if (ccw.count > vdev->config_len) {
ret = -EINVAL;
break;
}
}
- len = MIN(ccw.count, dev->vdev->config_len);
+ len = MIN(ccw.count, vdev->config_len);
hw_len = len;
if (!ccw.cda) {
ret = -EFAULT;
@@ -371,9 +375,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
} else {
len = hw_len;
/* XXX config space endianness */
- memcpy(dev->vdev->config, config, len);
+ memcpy(vdev->config, config, len);
cpu_physical_memory_unmap(config, hw_len, 0, hw_len);
- virtio_bus_set_vdev_config(&dev->bus, dev->vdev->config);
+ virtio_bus_set_vdev_config(&dev->bus, vdev->config);
sch->curr_status.scsw.count = ccw.count - len;
ret = 0;
}
@@ -397,9 +401,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
virtio_ccw_stop_ioeventfd(dev);
}
- virtio_set_status(dev->vdev, status);
- if (dev->vdev->status == 0) {
- virtio_reset(dev->vdev);
+ virtio_set_status(vdev, status);
+ if (vdev->status == 0) {
+ virtio_reset(vdev);
}
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
virtio_ccw_start_ioeventfd(dev);
@@ -463,7 +467,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
ret = -EFAULT;
} else {
vq_config.index = lduw_phys(ccw.cda);
- vq_config.num_max = virtio_queue_get_num(dev->vdev,
+ vq_config.num_max = virtio_queue_get_num(vdev,
vq_config.index);
stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max);
sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
@@ -495,7 +499,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
sch->driver_data = dev;
dev->sch = sch;
- dev->vdev = vdev;
dev->indicators = 0;
/* Initialize subchannel structure. */
@@ -608,7 +611,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
memset(&sch->id, 0, sizeof(SenseId));
sch->id.reserved = 0xff;
sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
- sch->id.cu_model = dev->vdev->device_id;
+ sch->id.cu_model = vdev->device_id;
/* Only the first 32 feature bits are used. */
dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus,
@@ -631,7 +634,6 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
{
SubchDev *sch = dev->sch;
- virtio_ccw_stop_ioeventfd(dev);
if (sch) {
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
g_free(sch);
@@ -892,9 +894,10 @@ static unsigned virtio_ccw_get_features(DeviceState *d)
static void virtio_ccw_reset(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
virtio_ccw_stop_ioeventfd(dev);
- virtio_reset(dev->vdev);
+ virtio_reset(vdev);
css_reset_sch(dev->sch);
dev->indicators = 0;
dev->indicators2 = 0;
@@ -934,9 +937,10 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
bool assign, bool with_irqfd)
{
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(dev->vdev);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (assign) {
int r = event_notifier_init(notifier, 0);
@@ -952,16 +956,16 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
* land in qemu (and only the irq fd) in this code.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(dev->vdev, n, false);
+ k->guest_notifier_mask(vdev, n, false);
}
/* get lost events and re-inject */
if (k->guest_notifier_pending &&
- k->guest_notifier_pending(dev->vdev, n)) {
+ k->guest_notifier_pending(vdev, n)) {
event_notifier_set(notifier);
}
} else {
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(dev->vdev, n, true);
+ k->guest_notifier_mask(vdev, n, true);
}
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
event_notifier_cleanup(notifier);
@@ -973,7 +977,7 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
bool assigned)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
- VirtIODevice *vdev = dev->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
int r, n;
for (n = 0; n < nvqs; n++) {
@@ -1228,6 +1232,8 @@ static int virtio_ccw_busdev_unplug(DeviceState *dev)
VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
SubchDev *sch = _dev->sch;
+ virtio_ccw_stop_ioeventfd(_dev);
+
/*
* We should arrive here only for device_del, since we don't support
* direct hot(un)plug of channels, but only through virtio.
@@ -1274,11 +1280,9 @@ static int virtual_css_bridge_init(SysBusDevice *dev)
static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = virtual_css_bridge_init;
- dc->no_user = 1;
}
static const TypeInfo virtual_css_bridge_info = {
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 96d6f5d5b7..00932c746d 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -77,7 +77,6 @@ typedef struct VirtIOCCWDeviceClass {
struct VirtioCcwDevice {
DeviceState parent_obj;
SubchDev *sch;
- VirtIODevice *vdev;
char *bus_id;
uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
VirtioBusState bus;
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 3e5e31da8f..50b89ad4aa 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -469,6 +469,8 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
r->req.dev->sense_is_ua = false;
}
break;
+ case TEST_UNIT_READY:
+ break;
default:
scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
scsi_req_complete(req, CHECK_CONDITION);
@@ -886,7 +888,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case RELEASE:
case ERASE:
case ALLOW_MEDIUM_REMOVAL:
- case VERIFY_10:
case SEEK_10:
case SYNCHRONIZE_CACHE:
case SYNCHRONIZE_CACHE_16:
@@ -903,6 +904,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
case ALLOW_OVERWRITE:
cmd->xfer = 0;
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ if ((buf[1] & 2) == 0) {
+ cmd->xfer = 0;
+ } else if ((buf[1] & 4) == 1) {
+ cmd->xfer = 1;
+ }
+ cmd->xfer *= dev->blocksize;
+ break;
case MODE_SENSE:
break;
case WRITE_SAME_10:
@@ -1100,6 +1111,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
case WRITE_VERIFY_12:
case WRITE_16:
case WRITE_VERIFY_16:
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
case COPY:
case COPY_VERIFY:
case COMPARE:
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index efadfc023f..a8d0f15ebe 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -47,6 +47,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define SCSI_MAX_MODE_LEN 256
#define DEFAULT_DISCARD_GRANULARITY 4096
+#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */
typedef struct SCSIDiskState SCSIDiskState;
@@ -74,6 +75,7 @@ struct SCSIDiskState
bool media_event;
bool eject_request;
uint64_t wwn;
+ uint64_t max_unmap_size;
QEMUBH *bh;
char *version;
char *serial;
@@ -625,6 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
s->qdev.conf.min_io_size / s->qdev.blocksize;
unsigned int opt_io_size =
s->qdev.conf.opt_io_size / s->qdev.blocksize;
+ unsigned int max_unmap_sectors =
+ s->max_unmap_size / s->qdev.blocksize;
if (s->qdev.type == TYPE_ROM) {
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
@@ -647,6 +651,18 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
outbuf[14] = (opt_io_size >> 8) & 0xff;
outbuf[15] = opt_io_size & 0xff;
+ /* max unmap LBA count, default is 1GB */
+ outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
+ outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
+ outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
+ outbuf[23] = max_unmap_sectors & 0xff;
+
+ /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */
+ outbuf[24] = 0;
+ outbuf[25] = 0;
+ outbuf[26] = 0;
+ outbuf[27] = 255;
+
/* optimal unmap granularity */
outbuf[28] = (unmap_sectors >> 24) & 0xff;
outbuf[29] = (unmap_sectors >> 16) & 0xff;
@@ -1626,7 +1642,7 @@ static void scsi_write_same_complete(void *opaque, int ret)
bdrv_acct_start(s->qdev.conf.bs, &r->acct, data->iov.iov_len, BDRV_ACCT_WRITE);
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, data->sector,
&data->qiov, data->iov.iov_len / 512,
- scsi_write_same_complete, r);
+ scsi_write_same_complete, data);
return;
}
@@ -1720,10 +1736,19 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
scsi_disk_emulate_unmap(r, r->iov.iov_base);
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ if (r->req.status == -1) {
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+ }
+ break;
+
case WRITE_SAME_10:
case WRITE_SAME_16:
scsi_disk_emulate_write_same(r, r->iov.iov_base);
break;
+
default:
abort();
}
@@ -1964,6 +1989,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
case UNMAP:
DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3);
+ if (req->cmd.buf[1] & 6) {
+ goto illegal_request;
+ }
+ break;
case WRITE_SAME_10:
case WRITE_SAME_16:
DPRINTF("WRITE SAME %d (len %lu)\n",
@@ -2044,10 +2077,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return 0;
}
- /* fallthrough */
- case VERIFY_10:
- case VERIFY_12:
- case VERIFY_16:
DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
@@ -2225,7 +2254,7 @@ static int scsi_initfn(SCSIDevice *dev)
} else {
bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
}
- bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
+ bdrv_set_guest_block_size(s->qdev.conf.bs, s->qdev.blocksize);
bdrv_iostatus_enable(s->qdev.conf.bs);
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
@@ -2277,6 +2306,7 @@ static const SCSIReqOps scsi_disk_emulate_reqops = {
.send_command = scsi_disk_emulate_command,
.read_data = scsi_disk_emulate_read_data,
.write_data = scsi_disk_emulate_write_data,
+ .cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
};
@@ -2315,14 +2345,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
[UNMAP] = &scsi_disk_emulate_reqops,
[WRITE_SAME_10] = &scsi_disk_emulate_reqops,
[WRITE_SAME_16] = &scsi_disk_emulate_reqops,
+ [VERIFY_10] = &scsi_disk_emulate_reqops,
+ [VERIFY_12] = &scsi_disk_emulate_reqops,
+ [VERIFY_16] = &scsi_disk_emulate_reqops,
[READ_6] = &scsi_disk_dma_reqops,
[READ_10] = &scsi_disk_dma_reqops,
[READ_12] = &scsi_disk_dma_reqops,
[READ_16] = &scsi_disk_dma_reqops,
- [VERIFY_10] = &scsi_disk_dma_reqops,
- [VERIFY_12] = &scsi_disk_dma_reqops,
- [VERIFY_16] = &scsi_disk_dma_reqops,
[WRITE_6] = &scsi_disk_dma_reqops,
[WRITE_10] = &scsi_disk_dma_reqops,
[WRITE_12] = &scsi_disk_dma_reqops,
@@ -2506,6 +2536,8 @@ static Property scsi_hd_properties[] = {
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+ DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
+ DEFAULT_MAX_UNMAP_SIZE),
DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2615,6 +2647,8 @@ static Property scsi_disk_properties[] = {
DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
SCSI_DISK_F_DPOFUA, false),
DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+ DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
+ DEFAULT_MAX_UNMAP_SIZE),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 8f195bec00..f08b64e177 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -210,7 +210,7 @@ static void scsi_read_complete(void * opaque, int ret)
s->blocksize = ldl_be_p(&r->buf[8]);
s->max_lba = ldq_be_p(&r->buf[0]);
}
- bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
+ bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
scsi_req_data(&r->req, len);
if (!r->req.io_canceled) {
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 9e770fba98..3983a5b464 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -196,29 +196,31 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
}
}
-static int vhost_scsi_init(VirtIODevice *vdev)
+static void vhost_scsi_realize(DeviceState *dev, Error **errp)
{
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
- VHostSCSI *s = VHOST_SCSI(vdev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
+ VHostSCSI *s = VHOST_SCSI(dev);
+ Error *err = NULL;
int vhostfd = -1;
int ret;
if (!vs->conf.wwpn) {
- error_report("vhost-scsi: missing wwpn\n");
- return -EINVAL;
+ error_setg(errp, "vhost-scsi: missing wwpn");
+ return;
}
if (vs->conf.vhostfd) {
vhostfd = monitor_handle_fd_param(cur_mon, vs->conf.vhostfd);
if (vhostfd == -1) {
- error_report("vhost-scsi: unable to parse vhostfd\n");
- return -EINVAL;
+ error_setg(errp, "vhost-scsi: unable to parse vhostfd");
+ return;
}
}
- ret = virtio_scsi_common_init(vs);
- if (ret < 0) {
- return ret;
+ virtio_scsi_common_realize(dev, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
}
s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
@@ -227,24 +229,21 @@ static int vhost_scsi_init(VirtIODevice *vdev)
ret = vhost_dev_init(&s->dev, vhostfd, "/dev/vhost-scsi", true);
if (ret < 0) {
- error_report("vhost-scsi: vhost initialization failed: %s\n",
- strerror(-ret));
- return ret;
+ error_setg(errp, "vhost-scsi: vhost initialization failed: %s",
+ strerror(-ret));
+ return;
}
s->dev.backend_features = 0;
error_setg(&s->migration_blocker,
"vhost-scsi does not support migration");
migrate_add_blocker(s->migration_blocker);
-
- return 0;
}
-static int vhost_scsi_exit(DeviceState *qdev)
+static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
- VHostSCSI *s = VHOST_SCSI(qdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostSCSI *s = VHOST_SCSI(dev);
migrate_del_blocker(s->migration_blocker);
error_free(s->migration_blocker);
@@ -253,7 +252,8 @@ static int vhost_scsi_exit(DeviceState *qdev)
vhost_scsi_set_status(vdev, 0);
g_free(s->dev.vqs);
- return virtio_scsi_common_exit(vs);
+
+ virtio_scsi_common_unrealize(dev, errp);
}
static Property vhost_scsi_properties[] = {
@@ -265,10 +265,11 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = vhost_scsi_exit;
+
dc->props = vhost_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- vdc->init = vhost_scsi_init;
+ vdc->realize = vhost_scsi_realize;
+ vdc->unrealize = vhost_scsi_unrealize;
vdc->get_features = vhost_scsi_get_features;
vdc->set_config = vhost_scsi_set_config;
vdc->set_status = vhost_scsi_set_status;
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 26d95a14ec..6610b3aab3 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -306,6 +306,10 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
VirtIOSCSIReq *req = r->hba_private;
uint32_t sense_len;
+ if (r->io_canceled) {
+ return;
+ }
+
req->resp.cmd->response = VIRTIO_SCSI_S_OK;
req->resp.cmd->status = status;
if (req->resp.cmd->status == GOOD) {
@@ -516,7 +520,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
evt->event = event;
evt->reason = reason;
if (!dev) {
- assert(event == VIRTIO_SCSI_T_NO_EVENT);
+ assert(event == VIRTIO_SCSI_T_EVENTS_MISSED);
} else {
evt->lun[0] = 1;
evt->lun[1] = dev->id;
@@ -589,12 +593,13 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
.load_request = virtio_scsi_load_request,
};
-int virtio_scsi_common_init(VirtIOSCSICommon *s)
+void virtio_scsi_common_realize(DeviceState *dev, Error **errp)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev);
int i;
- virtio_init(VIRTIO_DEVICE(s), "virtio-scsi", VIRTIO_ID_SCSI,
+ virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI,
sizeof(VirtIOSCSIConfig));
s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *));
@@ -609,57 +614,52 @@ int virtio_scsi_common_init(VirtIOSCSICommon *s)
s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE,
virtio_scsi_handle_cmd);
}
-
- return 0;
}
-static int virtio_scsi_device_init(VirtIODevice *vdev)
+static void virtio_scsi_device_realize(DeviceState *dev, Error **errp)
{
- DeviceState *qdev = DEVICE(vdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSCSI *s = VIRTIO_SCSI(dev);
static int virtio_scsi_id;
Error *err = NULL;
- int ret;
- ret = virtio_scsi_common_init(vs);
- if (ret < 0) {
- return ret;
+ virtio_scsi_common_realize(dev, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
}
- scsi_bus_new(&s->bus, sizeof(s->bus), qdev,
+ scsi_bus_new(&s->bus, sizeof(s->bus), dev,
&virtio_scsi_scsi_info, vdev->bus_name);
- if (!qdev->hotplugged) {
+ if (!dev->hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus, &err);
if (err != NULL) {
- error_free(err);
- return -1;
+ error_propagate(errp, err);
+ return;
}
}
- register_savevm(qdev, "virtio-scsi", virtio_scsi_id++, 1,
+ register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1,
virtio_scsi_save, virtio_scsi_load, s);
-
- return 0;
}
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs)
+void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(vs);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
g_free(vs->cmd_vqs);
virtio_cleanup(vdev);
- return 0;
}
-static int virtio_scsi_device_exit(DeviceState *qdev)
+static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIOSCSI *s = VIRTIO_SCSI(qdev);
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
+ VirtIOSCSI *s = VIRTIO_SCSI(dev);
- unregister_savevm(qdev, "virtio-scsi", s);
- return virtio_scsi_common_exit(vs);
+ unregister_savevm(dev, "virtio-scsi", s);
+
+ virtio_scsi_common_unrealize(dev, errp);
}
static Property virtio_scsi_properties[] = {
@@ -680,10 +680,11 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_scsi_device_exit;
+
dc->props = virtio_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- vdc->init = virtio_scsi_device_init;
+ vdc->realize = virtio_scsi_device_realize;
+ vdc->unrealize = virtio_scsi_device_unrealize;
vdc->set_config = virtio_scsi_set_config;
vdc->get_features = virtio_scsi_get_features;
vdc->reset = virtio_scsi_reset;
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index c35896d28c..462558b76d 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -506,7 +506,6 @@ static void pl181_class_init(ObjectClass *klass, void *data)
sdc->init = pl181_init;
k->vmsd = &vmstate_pl181;
k->reset = pl181_reset;
- k->no_user = 1;
}
static const TypeInfo pl181_info = {
diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c
index e81176a11e..a2f6d9e0b6 100644
--- a/hw/sh4/sh_pci.c
+++ b/hw/sh4/sh_pci.c
@@ -162,10 +162,16 @@ static int sh_pci_host_init(PCIDevice *d)
static void sh_pci_host_class_init(ObjectClass *klass, void *data)
{
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = sh_pci_host_init;
k->vendor_id = PCI_VENDOR_ID_HITACHI;
k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo sh_pci_host_info = {
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 3ae091c95e..2c86c3d412 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -26,5 +26,8 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_TUSB6010) += tusb6010.o
+obj-$(CONFIG_DIGIC) += digic-timer.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
+
+obj-$(CONFIG_ALLWINNER_A10_PIT) += allwinner-a10-pit.o
diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c
new file mode 100644
index 0000000000..b27fce8cd2
--- /dev/null
+++ b/hw/timer/allwinner-a10-pit.c
@@ -0,0 +1,254 @@
+/*
+ * Allwinner A10 timer device emulation
+ *
+ * Copyright (C) 2013 Li Guang
+ * Written by Li Guang <lig.fnst@cn.fujitsu.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/timer/allwinner-a10-pit.h"
+
+static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AwA10PITState *s = AW_A10_PIT(opaque);
+ uint8_t index;
+
+ switch (offset) {
+ case AW_A10_PIT_TIMER_IRQ_EN:
+ return s->irq_enable;
+ case AW_A10_PIT_TIMER_IRQ_ST:
+ return s->irq_status;
+ case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
+ index = offset & 0xf0;
+ index >>= 4;
+ index -= 1;
+ switch (offset & 0x0f) {
+ case AW_A10_PIT_TIMER_CONTROL:
+ return s->control[index];
+ case AW_A10_PIT_TIMER_INTERVAL:
+ return s->interval[index];
+ case AW_A10_PIT_TIMER_COUNT:
+ s->count[index] = ptimer_get_count(s->timer[index]);
+ return s->count[index];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+ case AW_A10_PIT_WDOG_CONTROL:
+ break;
+ case AW_A10_PIT_WDOG_MODE:
+ break;
+ case AW_A10_PIT_COUNT_LO:
+ return s->count_lo;
+ case AW_A10_PIT_COUNT_HI:
+ return s->count_hi;
+ case AW_A10_PIT_COUNT_CTL:
+ return s->count_ctl;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+
+ return 0;
+}
+
+static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ AwA10PITState *s = AW_A10_PIT(opaque);
+ uint8_t index;
+
+ switch (offset) {
+ case AW_A10_PIT_TIMER_IRQ_EN:
+ s->irq_enable = value;
+ break;
+ case AW_A10_PIT_TIMER_IRQ_ST:
+ s->irq_status &= ~value;
+ break;
+ case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END:
+ index = offset & 0xf0;
+ index >>= 4;
+ index -= 1;
+ switch (offset & 0x0f) {
+ case AW_A10_PIT_TIMER_CONTROL:
+ s->control[index] = value;
+ if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
+ ptimer_set_count(s->timer[index], s->interval[index]);
+ }
+ if (s->control[index] & AW_A10_PIT_TIMER_EN) {
+ int oneshot = 0;
+ if (s->control[index] & AW_A10_PIT_TIMER_MODE) {
+ oneshot = 1;
+ }
+ ptimer_run(s->timer[index], oneshot);
+ } else {
+ ptimer_stop(s->timer[index]);
+ }
+ break;
+ case AW_A10_PIT_TIMER_INTERVAL:
+ s->interval[index] = value;
+ ptimer_set_limit(s->timer[index], s->interval[index], 1);
+ break;
+ case AW_A10_PIT_TIMER_COUNT:
+ s->count[index] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ }
+ break;
+ case AW_A10_PIT_WDOG_CONTROL:
+ s->watch_dog_control = value;
+ break;
+ case AW_A10_PIT_WDOG_MODE:
+ s->watch_dog_mode = value;
+ break;
+ case AW_A10_PIT_COUNT_LO:
+ s->count_lo = value;
+ break;
+ case AW_A10_PIT_COUNT_HI:
+ s->count_hi = value;
+ break;
+ case AW_A10_PIT_COUNT_CTL:
+ s->count_ctl = value;
+ if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) {
+ uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ s->count_lo = tmp_count;
+ s->count_hi = tmp_count >> 32;
+ s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN;
+ }
+ if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) {
+ s->count_lo = 0;
+ s->count_hi = 0;
+ s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%x\n", __func__, (int)offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps a10_pit_ops = {
+ .read = a10_pit_read,
+ .write = a10_pit_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_a10_pit = {
+ .name = "a10.pit",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(irq_enable, AwA10PITState),
+ VMSTATE_UINT32(irq_status, AwA10PITState),
+ VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR),
+ VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR),
+ VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR),
+ VMSTATE_UINT32(watch_dog_mode, AwA10PITState),
+ VMSTATE_UINT32(watch_dog_control, AwA10PITState),
+ VMSTATE_UINT32(count_lo, AwA10PITState),
+ VMSTATE_UINT32(count_hi, AwA10PITState),
+ VMSTATE_UINT32(count_ctl, AwA10PITState),
+ VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void a10_pit_reset(DeviceState *dev)
+{
+ AwA10PITState *s = AW_A10_PIT(dev);
+ uint8_t i;
+
+ s->irq_enable = 0;
+ s->irq_status = 0;
+ for (i = 0; i < 6; i++) {
+ s->control[i] = AW_A10_PIT_DEFAULT_CLOCK;
+ s->interval[i] = 0;
+ s->count[i] = 0;
+ ptimer_stop(s->timer[i]);
+ }
+ s->watch_dog_mode = 0;
+ s->watch_dog_control = 0;
+ s->count_lo = 0;
+ s->count_hi = 0;
+ s->count_ctl = 0;
+}
+
+static void a10_pit_timer_cb(void *opaque)
+{
+ AwA10PITState *s = AW_A10_PIT(opaque);
+ uint8_t i;
+
+ for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
+ if (s->control[i] & AW_A10_PIT_TIMER_EN) {
+ s->irq_status |= 1 << i;
+ if (s->control[i] & AW_A10_PIT_TIMER_MODE) {
+ ptimer_stop(s->timer[i]);
+ s->control[i] &= ~AW_A10_PIT_TIMER_EN;
+ }
+ qemu_irq_pulse(s->irq[i]);
+ }
+ }
+}
+
+static void a10_pit_init(Object *obj)
+{
+ AwA10PITState *s = AW_A10_PIT(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ QEMUBH * bh[AW_A10_PIT_TIMER_NR];
+ uint8_t i;
+
+ for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
+ sysbus_init_irq(sbd, &s->irq[i]);
+ }
+ memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s,
+ TYPE_AW_A10_PIT, 0x400);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) {
+ bh[i] = qemu_bh_new(a10_pit_timer_cb, s);
+ s->timer[i] = ptimer_init(bh[i]);
+ ptimer_set_freq(s->timer[i], 240000);
+ }
+}
+
+static void a10_pit_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = a10_pit_reset;
+ dc->desc = "allwinner a10 timer";
+ dc->vmsd = &vmstate_a10_pit;
+}
+
+static const TypeInfo a10_pit_info = {
+ .name = TYPE_AW_A10_PIT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AwA10PITState),
+ .instance_init = a10_pit_init,
+ .class_init = a10_pit_class_init,
+};
+
+static void a10_register_types(void)
+{
+ type_register_static(&a10_pit_info);
+}
+
+type_init(a10_register_types);
diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c
index d9f9494f26..35a0a2356f 100644
--- a/hw/timer/arm_mptimer.c
+++ b/hw/timer/arm_mptimer.c
@@ -274,7 +274,6 @@ static void arm_mptimer_class_init(ObjectClass *klass, void *data)
dc->realize = arm_mptimer_realize;
dc->vmsd = &vmstate_arm_mptimer;
dc->reset = arm_mptimer_reset;
- dc->no_user = 1;
dc->props = arm_mptimer_properties;
}
diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c
new file mode 100644
index 0000000000..1fde22c67f
--- /dev/null
+++ b/hw/timer/digic-timer.c
@@ -0,0 +1,163 @@
+/*
+ * QEMU model of the Canon DIGIC timer block.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This model is based on reverse engineering efforts
+ * made by CHDK (http://chdk.wikia.com) and
+ * Magic Lantern (http://www.magiclantern.fm) projects
+ * contributors.
+ *
+ * See "Timer/Clock Module" docs here:
+ * http://magiclantern.wikia.com/wiki/Register_Map
+ *
+ * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao
+ * is used as a template.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+#include "qemu/main-loop.h"
+
+#include "hw/timer/digic-timer.h"
+
+static const VMStateDescription vmstate_digic_timer = {
+ .name = "digic.timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer, DigicTimerState),
+ VMSTATE_UINT32(control, DigicTimerState),
+ VMSTATE_UINT32(relvalue, DigicTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void digic_timer_reset(DeviceState *dev)
+{
+ DigicTimerState *s = DIGIC_TIMER(dev);
+
+ ptimer_stop(s->ptimer);
+ s->control = 0;
+ s->relvalue = 0;
+}
+
+static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ DigicTimerState *s = opaque;
+ uint64_t ret = 0;
+
+ switch (offset) {
+ case DIGIC_TIMER_CONTROL:
+ ret = s->control;
+ break;
+ case DIGIC_TIMER_RELVALUE:
+ ret = s->relvalue;
+ break;
+ case DIGIC_TIMER_VALUE:
+ ret = ptimer_get_count(s->ptimer) & 0xffff;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "digic-timer: read access to unknown register 0x"
+ TARGET_FMT_plx, offset);
+ }
+
+ return ret;
+}
+
+static void digic_timer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ DigicTimerState *s = opaque;
+
+ switch (offset) {
+ case DIGIC_TIMER_CONTROL:
+ if (value & DIGIC_TIMER_CONTROL_RST) {
+ digic_timer_reset((DeviceState *)s);
+ break;
+ }
+
+ if (value & DIGIC_TIMER_CONTROL_EN) {
+ ptimer_run(s->ptimer, 0);
+ }
+
+ s->control = (uint32_t)value;
+ break;
+
+ case DIGIC_TIMER_RELVALUE:
+ s->relvalue = extract32(value, 0, 16);
+ ptimer_set_limit(s->ptimer, s->relvalue, 1);
+ break;
+
+ case DIGIC_TIMER_VALUE:
+ break;
+
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "digic-timer: read access to unknown register 0x"
+ TARGET_FMT_plx, offset);
+ }
+}
+
+static const MemoryRegionOps digic_timer_ops = {
+ .read = digic_timer_read,
+ .write = digic_timer_write,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void digic_timer_init(Object *obj)
+{
+ DigicTimerState *s = DIGIC_TIMER(obj);
+
+ s->ptimer = ptimer_init(NULL);
+
+ /*
+ * FIXME: there is no documentation on Digic timer
+ * frequency setup so let it always run at 1 MHz
+ */
+ ptimer_set_freq(s->ptimer, 1 * 1000 * 1000);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s,
+ TYPE_DIGIC_TIMER, 0x100);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static void digic_timer_class_init(ObjectClass *klass, void *class_data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = digic_timer_reset;
+ dc->vmsd = &vmstate_digic_timer;
+}
+
+static const TypeInfo digic_timer_info = {
+ .name = TYPE_DIGIC_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(DigicTimerState),
+ .instance_init = digic_timer_init,
+ .class_init = digic_timer_class_init,
+};
+
+static void digic_timer_register_type(void)
+{
+ type_register_static(&digic_timer_info);
+}
+
+type_init(digic_timer_register_type)
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index 2eb75ea945..2fbbeb1735 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -42,7 +42,6 @@
#define HPET_MSI_SUPPORT 0
-#define TYPE_HPET "hpet"
#define HPET(obj) OBJECT_CHECK(HPETState, (obj), TYPE_HPET)
struct HPETState;
@@ -73,6 +72,7 @@ typedef struct HPETState {
uint8_t rtc_irq_level;
qemu_irq pit_enabled;
uint8_t num_timers;
+ uint32_t intcap;
HPETTimer timer[HPET_MAX_TIMERS];
/* Memory-mapped, software visible registers */
@@ -198,13 +198,23 @@ static void update_irq(struct HPETTimer *timer, int set)
if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
s->isr &= ~mask;
if (!timer_fsb_route(timer)) {
- qemu_irq_lower(s->irqs[route]);
+ /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
+ if (route >= ISA_NUM_IRQS) {
+ qemu_irq_raise(s->irqs[route]);
+ } else {
+ qemu_irq_lower(s->irqs[route]);
+ }
}
} else if (timer_fsb_route(timer)) {
stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
- qemu_irq_raise(s->irqs[route]);
+ /* fold the ICH PIRQ# pin's internal inversion logic into hpet */
+ if (route >= ISA_NUM_IRQS) {
+ qemu_irq_lower(s->irqs[route]);
+ } else {
+ qemu_irq_raise(s->irqs[route]);
+ }
} else {
s->isr &= ~mask;
qemu_irq_pulse(s->irqs[route]);
@@ -653,8 +663,8 @@ static void hpet_reset(DeviceState *d)
if (s->flags & (1 << HPET_MSI_SUPPORT)) {
timer->config |= HPET_TN_FSB_CAP;
}
- /* advertise availability of ioapic inti2 */
- timer->config |= 0x00000004ULL << 32;
+ /* advertise availability of ioapic int */
+ timer->config |= (uint64_t)s->intcap << 32;
timer->period = 0ULL;
timer->wrap_flag = 0;
}
@@ -703,6 +713,9 @@ static void hpet_realize(DeviceState *dev, Error **errp)
int i;
HPETTimer *timer;
+ if (!s->intcap) {
+ error_printf("Hpet's intcap not initialized.\n");
+ }
if (hpet_cfg.count == UINT8_MAX) {
/* first instance */
hpet_cfg.count = 0;
@@ -743,6 +756,7 @@ static void hpet_realize(DeviceState *dev, Error **errp)
static Property hpet_device_properties[] = {
DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+ DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -751,17 +765,11 @@ static void hpet_device_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = hpet_realize;
- dc->no_user = 1;
dc->reset = hpet_reset;
dc->vmsd = &vmstate_hpet;
dc->props = hpet_device_properties;
}
-bool hpet_find(void)
-{
- return object_resolve_path_type("", TYPE_HPET, NULL);
-}
-
static const TypeInfo hpet_device_info = {
.name = TYPE_HPET,
.parent = TYPE_SYS_BUS_DEVICE,
diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c
index e8fb971488..9db5c9d129 100644
--- a/hw/timer/i8254_common.c
+++ b/hw/timer/i8254_common.c
@@ -282,7 +282,12 @@ static void pit_common_class_init(ObjectClass *klass, void *data)
dc->realize = pit_common_realize;
dc->vmsd = &vmstate_pit_common;
- dc->no_user = 1;
+ /*
+ * Reason: unlike ordinary ISA devices, the PIT may need to be
+ * wired to the HPET, and because of that, some wiring is always
+ * done by board code.
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo pit_common_type = {
diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c
index be0592b53d..3cfb18a8b3 100644
--- a/hw/timer/m48t59.c
+++ b/hw/timer/m48t59.c
@@ -750,9 +750,10 @@ static void m48t59_isa_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = m48t59_isa_realize;
- dc->no_user = 1;
dc->reset = m48t59_reset_isa;
dc->props = m48t59_isa_properties;
+ /* Reason: needs to be wired up by m48t59_init_isa() */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo m48t59_isa_info = {
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index b0116381c0..6fb124fead 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -899,9 +899,10 @@ static void rtc_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = rtc_realizefn;
- dc->no_user = 1;
dc->vmsd = &vmstate_rtc;
dc->props = mc146818rtc_properties;
+ /* Reason: needs to be wired up by rtc_init() */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo mc146818rtc_info = {
diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c
index 65928a4819..34d9b44e7e 100644
--- a/hw/timer/pl031.c
+++ b/hw/timer/pl031.c
@@ -251,7 +251,6 @@ static void pl031_class_init(ObjectClass *klass, void *data)
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = pl031_init;
- dc->no_user = 1;
dc->vmsd = &vmstate_pl031;
}
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index a3eac3e5c1..97b457541f 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -1,5 +1,5 @@
# usb subsystem core
-common-obj-y += core.o combined-packet.o bus.o desc.o
+common-obj-y += core.o combined-packet.o bus.o desc.o desc-msos.o
common-obj-y += libhw.o
# usb host adapters
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 09848c6320..fe70429304 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -16,6 +16,8 @@ static Property usb_props[] = {
DEFINE_PROP_STRING("serial", USBDevice, serial),
DEFINE_PROP_BIT("full-path", USBDevice, flags,
USB_DEV_FLAG_FULL_PATH, true),
+ DEFINE_PROP_BIT("msos-desc", USBDevice, flags,
+ USB_DEV_FLAG_MSOS_DESC_ENABLE, true),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c
new file mode 100644
index 0000000000..ed8d62cab8
--- /dev/null
+++ b/hw/usb/desc-msos.c
@@ -0,0 +1,234 @@
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+
+/*
+ * Microsoft OS Descriptors
+ *
+ * Windows tries to fetch some special descriptors with informations
+ * specifically for windows. Presence is indicated using a special
+ * string @ index 0xee. There are two kinds of descriptors:
+ *
+ * compatid descriptor
+ * Used to bind drivers, if usb class isn't specific enougth.
+ * Used for PTP/MTP for example (both share the same usb class).
+ *
+ * properties descriptor
+ * Does carry registry entries. They show up in
+ * HLM\SYSTEM\CurrentControlSet\Enum\USB\<devid>\<serial>\Device Parameters
+ *
+ * Note that Windows caches the stuff it got in the registry, so when
+ * playing with this you have to delete registry subtrees to make
+ * windows query the device again:
+ * HLM\SYSTEM\CurrentControlSet\Control\usbflags
+ * HLM\SYSTEM\CurrentControlSet\Enum\USB
+ * Windows will complain it can't delete entries on the second one.
+ * It has deleted everything it had permissions too, which is enouth
+ * as this includes "Device Parameters".
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx
+ *
+ */
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_compat_hdr {
+ uint32_t dwLength;
+ uint8_t bcdVersion_lo;
+ uint8_t bcdVersion_hi;
+ uint8_t wIndex_lo;
+ uint8_t wIndex_hi;
+ uint8_t bCount;
+ uint8_t reserved[7];
+} QEMU_PACKED msos_compat_hdr;
+
+typedef struct msos_compat_func {
+ uint8_t bFirstInterfaceNumber;
+ uint8_t reserved_1;
+ uint8_t compatibleId[8];
+ uint8_t subCompatibleId[8];
+ uint8_t reserved_2[6];
+} QEMU_PACKED msos_compat_func;
+
+static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest)
+{
+ msos_compat_hdr *hdr = (void *)dest;
+ msos_compat_func *func;
+ int length = sizeof(*hdr);
+ int count = 0;
+
+ func = (void *)(dest + length);
+ func->bFirstInterfaceNumber = 0;
+ func->reserved_1 = 0x01;
+ length += sizeof(*func);
+ count++;
+
+ hdr->dwLength = cpu_to_le32(length);
+ hdr->bcdVersion_lo = 0x00;
+ hdr->bcdVersion_hi = 0x01;
+ hdr->wIndex_lo = 0x04;
+ hdr->wIndex_hi = 0x00;
+ hdr->bCount = count;
+ return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+typedef struct msos_prop_hdr {
+ uint32_t dwLength;
+ uint8_t bcdVersion_lo;
+ uint8_t bcdVersion_hi;
+ uint8_t wIndex_lo;
+ uint8_t wIndex_hi;
+ uint8_t wCount_lo;
+ uint8_t wCount_hi;
+} QEMU_PACKED msos_prop_hdr;
+
+typedef struct msos_prop {
+ uint32_t dwLength;
+ uint32_t dwPropertyDataType;
+ uint8_t dwPropertyNameLength_lo;
+ uint8_t dwPropertyNameLength_hi;
+ uint8_t bPropertyName[];
+} QEMU_PACKED msos_prop;
+
+typedef struct msos_prop_data {
+ uint32_t dwPropertyDataLength;
+ uint8_t bPropertyData[];
+} QEMU_PACKED msos_prop_data;
+
+typedef enum msos_prop_type {
+ MSOS_REG_SZ = 1,
+ MSOS_REG_EXPAND_SZ = 2,
+ MSOS_REG_BINARY = 3,
+ MSOS_REG_DWORD_LE = 4,
+ MSOS_REG_DWORD_BE = 5,
+ MSOS_REG_LINK = 6,
+ MSOS_REG_MULTI_SZ = 7,
+} msos_prop_type;
+
+static int usb_desc_msos_prop_name(struct msos_prop *prop,
+ const wchar_t *name)
+{
+ int length = wcslen(name) + 1;
+ int i;
+
+ prop->dwPropertyNameLength_lo = usb_lo(length*2);
+ prop->dwPropertyNameLength_hi = usb_hi(length*2);
+ for (i = 0; i < length; i++) {
+ prop->bPropertyName[i*2] = usb_lo(name[i]);
+ prop->bPropertyName[i*2+1] = usb_hi(name[i]);
+ }
+ return length*2;
+}
+
+static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type,
+ const wchar_t *name, const wchar_t *value)
+{
+ struct msos_prop *prop = (void *)dest;
+ struct msos_prop_data *data;
+ int length = sizeof(*prop);
+ int i, vlen = wcslen(value) + 1;
+
+ prop->dwPropertyDataType = cpu_to_le32(type);
+ length += usb_desc_msos_prop_name(prop, name);
+ data = (void *)(dest + length);
+
+ data->dwPropertyDataLength = cpu_to_le32(vlen*2);
+ length += sizeof(*prop);
+
+ for (i = 0; i < vlen; i++) {
+ data->bPropertyData[i*2] = usb_lo(value[i]);
+ data->bPropertyData[i*2+1] = usb_hi(value[i]);
+ }
+ length += vlen*2;
+
+ prop->dwLength = cpu_to_le32(length);
+ return length;
+}
+
+static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name,
+ uint32_t value)
+{
+ struct msos_prop *prop = (void *)dest;
+ struct msos_prop_data *data;
+ int length = sizeof(*prop);
+
+ prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE);
+ length += usb_desc_msos_prop_name(prop, name);
+ data = (void *)(dest + length);
+
+ data->dwPropertyDataLength = cpu_to_le32(4);
+ data->bPropertyData[0] = (value) & 0xff;
+ data->bPropertyData[1] = (value >> 8) & 0xff;
+ data->bPropertyData[2] = (value >> 16) & 0xff;
+ data->bPropertyData[3] = (value >> 24) & 0xff;
+ length += sizeof(*prop) + 4;
+
+ prop->dwLength = cpu_to_le32(length);
+ return length;
+}
+
+static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest)
+{
+ msos_prop_hdr *hdr = (void *)dest;
+ int length = sizeof(*hdr);
+ int count = 0;
+
+ if (desc->msos->Label) {
+ /*
+ * Given as example in the specs. Havn't figured yet where
+ * this label shows up in the windows gui.
+ */
+ length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ,
+ L"Label", desc->msos->Label);
+ count++;
+ }
+
+ if (desc->msos->SelectiveSuspendEnabled) {
+ /*
+ * Signaling remote wakeup capability in the standard usb
+ * descriptors isn't enouth to make windows actually use it.
+ * This is the "Yes, we really mean it" registy entry to flip
+ * the switch in the windows drivers.
+ */
+ length += usb_desc_msos_prop_dword(dest+length,
+ L"SelectiveSuspendEnabled", 1);
+ count++;
+ }
+
+ hdr->dwLength = cpu_to_le32(length);
+ hdr->bcdVersion_lo = 0x00;
+ hdr->bcdVersion_hi = 0x01;
+ hdr->wIndex_lo = 0x05;
+ hdr->wIndex_hi = 0x00;
+ hdr->wCount_lo = usb_lo(count);
+ hdr->wCount_hi = usb_hi(count);
+ return length;
+}
+
+/* ------------------------------------------------------------------ */
+
+int usb_desc_msos(const USBDesc *desc, USBPacket *p,
+ int index, uint8_t *dest, size_t len)
+{
+ void *buf = g_malloc0(4096);
+ int length = 0;
+
+ switch (index) {
+ case 0x0004:
+ length = usb_desc_msos_compat(desc, buf);
+ break;
+ case 0x0005:
+ length = usb_desc_msos_prop(desc, buf);
+ break;
+ }
+
+ if (length > len) {
+ length = len;
+ }
+ memcpy(dest, buf, length);
+ free(buf);
+
+ p->actual_length = length;
+ return 0;
+}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index f18a043500..f133ddb9db 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -7,7 +7,7 @@
/* ------------------------------------------------------------------ */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
- uint8_t *dest, size_t len)
+ bool msos, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x12;
USBDescriptor *d = (void *)dest;
@@ -19,8 +19,18 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
d->bLength = bLength;
d->bDescriptorType = USB_DT_DEVICE;
- d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
- d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ if (msos && dev->bcdUSB < 0x0200) {
+ /*
+ * Version 2.0+ required for microsoft os descriptors to work.
+ * Done this way so msos-desc compat property will handle both
+ * the version and the new descriptors being present.
+ */
+ d->u.device.bcdUSB_lo = usb_lo(0x0200);
+ d->u.device.bcdUSB_hi = usb_hi(0x0200);
+ } else {
+ d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
+ d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ }
d->u.device.bDeviceClass = dev->bDeviceClass;
d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
@@ -499,6 +509,10 @@ void usb_desc_init(USBDevice *dev)
if (desc->super) {
dev->speedmask |= USB_SPEED_MASK_SUPER;
}
+ if (desc->msos && (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_ENABLE))) {
+ dev->flags |= (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE);
+ usb_desc_set_string(dev, 0xee, "MSFT100Q");
+ }
usb_desc_setdefaults(dev);
}
@@ -626,6 +640,7 @@ int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int value, uint8_t *dest, size_t len)
{
+ bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
const USBDesc *desc = usb_device_get_usb_desc(dev);
const USBDescDevice *other_dev;
uint8_t buf[256];
@@ -646,7 +661,7 @@ int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
switch(type) {
case USB_DT_DEVICE:
- ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
+ ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf));
trace_usb_desc_device(dev->addr, len, ret);
break;
case USB_DT_CONFIG:
@@ -703,6 +718,7 @@ int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
+ bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
const USBDesc *desc = usb_device_get_usb_desc(dev);
int ret = -1;
@@ -782,6 +798,19 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
trace_usb_set_interface(dev->addr, index, value, ret);
break;
+ case VendorDeviceRequest | 'Q':
+ if (msos) {
+ ret = usb_desc_msos(desc, p, index, data, length);
+ trace_usb_desc_msos(dev->addr, index, length, ret);
+ }
+ break;
+ case VendorInterfaceRequest | 'Q':
+ if (msos) {
+ ret = usb_desc_msos(desc, p, index, data, length);
+ trace_usb_desc_msos(dev->addr, index, length, ret);
+ }
+ break;
+
}
return ret;
}
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index 81327b0e74..2b4fcdae76 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -2,6 +2,7 @@
#define QEMU_HW_USB_DESC_H
#include <inttypes.h>
+#include <wchar.h>
/* binary representation */
typedef struct USBDescriptor {
@@ -182,6 +183,11 @@ struct USBDescOther {
const uint8_t *data;
};
+struct USBDescMSOS {
+ const wchar_t *Label;
+ bool SelectiveSuspendEnabled;
+};
+
typedef const char *USBDescStrings[256];
struct USBDesc {
@@ -190,6 +196,7 @@ struct USBDesc {
const USBDescDevice *high;
const USBDescDevice *super;
const char* const *str;
+ const USBDescMSOS *msos;
};
#define USB_DESC_FLAG_SUPER (1 << 1)
@@ -207,7 +214,7 @@ static inline uint8_t usb_hi(uint16_t val)
/* generate usb packages from structs */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
- uint8_t *dest, size_t len);
+ bool msos, uint8_t *dest, size_t len);
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_config(const USBDescConfig *conf, int flags,
@@ -219,6 +226,8 @@ int usb_desc_iface(const USBDescIface *iface, int flags,
int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
uint8_t *dest, size_t len);
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
+int usb_desc_msos(const USBDesc *desc, USBPacket *p,
+ int index, uint8_t *dest, size_t len);
/* control message emulation helpers */
void usb_desc_init(USBDevice *dev);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 5e667f0199..2966066682 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -261,6 +261,10 @@ static const USBDescDevice desc_device_keyboard = {
},
};
+static const USBDescMSOS desc_msos_suspend = {
+ .SelectiveSuspendEnabled = true,
+};
+
static const USBDesc desc_mouse = {
.id = {
.idVendor = 0x0627,
@@ -272,6 +276,7 @@ static const USBDesc desc_mouse = {
},
.full = &desc_device_mouse,
.str = desc_strings,
+ .msos = &desc_msos_suspend,
};
static const USBDesc desc_tablet = {
@@ -285,6 +290,7 @@ static const USBDesc desc_tablet = {
},
.full = &desc_device_tablet,
.str = desc_strings,
+ .msos = &desc_msos_suspend,
};
static const USBDesc desc_tablet2 = {
@@ -299,6 +305,7 @@ static const USBDesc desc_tablet2 = {
.full = &desc_device_tablet,
.high = &desc_device_tablet2,
.str = desc_strings,
+ .msos = &desc_msos_suspend,
};
static const USBDesc desc_keyboard = {
@@ -312,6 +319,7 @@ static const USBDesc desc_keyboard = {
},
.full = &desc_device_keyboard,
.str = desc_strings,
+ .msos = &desc_msos_suspend,
};
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
diff --git a/hw/virtio/dataplane/Makefile.objs b/hw/virtio/dataplane/Makefile.objs
index a91bf33c8b..9a8cfc0297 100644
--- a/hw/virtio/dataplane/Makefile.objs
+++ b/hw/virtio/dataplane/Makefile.objs
@@ -1 +1 @@
-common-obj-y += hostmem.o vring.o
+common-obj-y += vring.o
diff --git a/hw/virtio/dataplane/hostmem.c b/hw/virtio/dataplane/hostmem.c
deleted file mode 100644
index 901d98b8a0..0000000000
--- a/hw/virtio/dataplane/hostmem.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Thread-safe guest to host memory mapping
- *
- * Copyright 2012 Red Hat, Inc. and/or its affiliates
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@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 "exec/address-spaces.h"
-#include "hw/virtio/dataplane/hostmem.h"
-
-static int hostmem_lookup_cmp(const void *phys_, const void *region_)
-{
- hwaddr phys = *(const hwaddr *)phys_;
- const HostMemRegion *region = region_;
-
- if (phys < region->guest_addr) {
- return -1;
- } else if (phys >= region->guest_addr + region->size) {
- return 1;
- } else {
- return 0;
- }
-}
-
-/**
- * Map guest physical address to host pointer
- */
-void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write)
-{
- HostMemRegion *region;
- void *host_addr = NULL;
- hwaddr offset_within_region;
-
- qemu_mutex_lock(&hostmem->current_regions_lock);
- region = bsearch(&phys, hostmem->current_regions,
- hostmem->num_current_regions,
- sizeof(hostmem->current_regions[0]),
- hostmem_lookup_cmp);
- if (!region) {
- goto out;
- }
- if (is_write && region->readonly) {
- goto out;
- }
- offset_within_region = phys - region->guest_addr;
- if (len <= region->size - offset_within_region) {
- host_addr = region->host_addr + offset_within_region;
- }
-out:
- qemu_mutex_unlock(&hostmem->current_regions_lock);
-
- return host_addr;
-}
-
-/**
- * Install new regions list
- */
-static void hostmem_listener_commit(MemoryListener *listener)
-{
- HostMem *hostmem = container_of(listener, HostMem, listener);
- int i;
-
- qemu_mutex_lock(&hostmem->current_regions_lock);
- for (i = 0; i < hostmem->num_current_regions; i++) {
- memory_region_unref(hostmem->current_regions[i].mr);
- }
- g_free(hostmem->current_regions);
- hostmem->current_regions = hostmem->new_regions;
- hostmem->num_current_regions = hostmem->num_new_regions;
- qemu_mutex_unlock(&hostmem->current_regions_lock);
-
- /* Reset new regions list */
- hostmem->new_regions = NULL;
- hostmem->num_new_regions = 0;
-}
-
-/**
- * Add a MemoryRegionSection to the new regions list
- */
-static void hostmem_append_new_region(HostMem *hostmem,
- MemoryRegionSection *section)
-{
- void *ram_ptr = memory_region_get_ram_ptr(section->mr);
- size_t num = hostmem->num_new_regions;
- size_t new_size = (num + 1) * sizeof(hostmem->new_regions[0]);
-
- hostmem->new_regions = g_realloc(hostmem->new_regions, new_size);
- hostmem->new_regions[num] = (HostMemRegion){
- .host_addr = ram_ptr + section->offset_within_region,
- .guest_addr = section->offset_within_address_space,
- .size = int128_get64(section->size),
- .readonly = section->readonly,
- .mr = section->mr,
- };
- hostmem->num_new_regions++;
-
- memory_region_ref(section->mr);
-}
-
-static void hostmem_listener_append_region(MemoryListener *listener,
- MemoryRegionSection *section)
-{
- HostMem *hostmem = container_of(listener, HostMem, listener);
-
- /* Ignore non-RAM regions, we may not be able to map them */
- if (!memory_region_is_ram(section->mr)) {
- return;
- }
-
- /* Ignore regions with dirty logging, we cannot mark them dirty */
- if (memory_region_is_logging(section->mr)) {
- return;
- }
-
- hostmem_append_new_region(hostmem, section);
-}
-
-/* We don't implement most MemoryListener callbacks, use these nop stubs */
-static void hostmem_listener_dummy(MemoryListener *listener)
-{
-}
-
-static void hostmem_listener_section_dummy(MemoryListener *listener,
- MemoryRegionSection *section)
-{
-}
-
-static void hostmem_listener_eventfd_dummy(MemoryListener *listener,
- MemoryRegionSection *section,
- bool match_data, uint64_t data,
- EventNotifier *e)
-{
-}
-
-static void hostmem_listener_coalesced_mmio_dummy(MemoryListener *listener,
- MemoryRegionSection *section,
- hwaddr addr, hwaddr len)
-{
-}
-
-void hostmem_init(HostMem *hostmem)
-{
- memset(hostmem, 0, sizeof(*hostmem));
-
- qemu_mutex_init(&hostmem->current_regions_lock);
-
- hostmem->listener = (MemoryListener){
- .begin = hostmem_listener_dummy,
- .commit = hostmem_listener_commit,
- .region_add = hostmem_listener_append_region,
- .region_del = hostmem_listener_section_dummy,
- .region_nop = hostmem_listener_append_region,
- .log_start = hostmem_listener_section_dummy,
- .log_stop = hostmem_listener_section_dummy,
- .log_sync = hostmem_listener_section_dummy,
- .log_global_start = hostmem_listener_dummy,
- .log_global_stop = hostmem_listener_dummy,
- .eventfd_add = hostmem_listener_eventfd_dummy,
- .eventfd_del = hostmem_listener_eventfd_dummy,
- .coalesced_mmio_add = hostmem_listener_coalesced_mmio_dummy,
- .coalesced_mmio_del = hostmem_listener_coalesced_mmio_dummy,
- .priority = 10,
- };
-
- memory_listener_register(&hostmem->listener, &address_space_memory);
- if (hostmem->num_new_regions > 0) {
- hostmem_listener_commit(&hostmem->listener);
- }
-}
-
-void hostmem_finalize(HostMem *hostmem)
-{
- memory_listener_unregister(&hostmem->listener);
- g_free(hostmem->new_regions);
- g_free(hostmem->current_regions);
- qemu_mutex_destroy(&hostmem->current_regions_lock);
-}
diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c
index 351a343806..665a1ffcb3 100644
--- a/hw/virtio/dataplane/vring.c
+++ b/hw/virtio/dataplane/vring.c
@@ -15,9 +15,53 @@
*/
#include "trace.h"
+#include "hw/hw.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
#include "hw/virtio/dataplane/vring.h"
#include "qemu/error-report.h"
+/* vring_map can be coupled with vring_unmap or (if you still have the
+ * value returned in *mr) memory_region_unref.
+ */
+static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
+ bool is_write)
+{
+ MemoryRegionSection section = memory_region_find(get_system_memory(), phys, len);
+
+ if (!section.mr || int128_get64(section.size) < len) {
+ goto out;
+ }
+ if (is_write && section.readonly) {
+ goto out;
+ }
+ if (!memory_region_is_ram(section.mr)) {
+ goto out;
+ }
+
+ /* Ignore regions with dirty logging, we cannot mark them dirty */
+ if (memory_region_is_logging(section.mr)) {
+ goto out;
+ }
+
+ *mr = section.mr;
+ return memory_region_get_ram_ptr(section.mr) + section.offset_within_region;
+
+out:
+ memory_region_unref(section.mr);
+ *mr = NULL;
+ return NULL;
+}
+
+static void vring_unmap(void *buffer, bool is_write)
+{
+ ram_addr_t addr;
+ MemoryRegion *mr;
+
+ mr = qemu_ram_addr_from_host(buffer, &addr);
+ memory_region_unref(mr);
+}
+
/* Map the guest's vring to host memory */
bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
{
@@ -27,8 +71,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
vring->broken = false;
- hostmem_init(&vring->hostmem);
- vring_ptr = hostmem_lookup(&vring->hostmem, vring_addr, vring_size, true);
+ vring_ptr = vring_map(&vring->mr, vring_addr, vring_size, true);
if (!vring_ptr) {
error_report("Failed to map vring "
"addr %#" HWADDR_PRIx " size %" HWADDR_PRIu,
@@ -54,7 +97,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n)
virtio_queue_set_last_avail_idx(vdev, n, vring->last_avail_idx);
virtio_queue_invalidate_signalled_used(vdev, n);
- hostmem_finalize(&vring->hostmem);
+ memory_region_unref(vring->mr);
}
/* Disable guest->host notifies */
@@ -110,14 +153,61 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
return vring_need_event(vring_used_event(&vring->vr), new, old);
}
+
+static int get_desc(Vring *vring, VirtQueueElement *elem,
+ struct vring_desc *desc)
+{
+ unsigned *num;
+ struct iovec *iov;
+ hwaddr *addr;
+ MemoryRegion *mr;
+
+ if (desc->flags & VRING_DESC_F_WRITE) {
+ num = &elem->in_num;
+ iov = &elem->in_sg[*num];
+ addr = &elem->in_addr[*num];
+ } else {
+ num = &elem->out_num;
+ iov = &elem->out_sg[*num];
+ addr = &elem->out_addr[*num];
+
+ /* If it's an output descriptor, they're all supposed
+ * to come before any input descriptors. */
+ if (unlikely(elem->in_num)) {
+ error_report("Descriptor has out after in");
+ return -EFAULT;
+ }
+ }
+
+ /* Stop for now if there are not enough iovecs available. */
+ if (*num >= VIRTQUEUE_MAX_SIZE) {
+ return -ENOBUFS;
+ }
+
+ /* TODO handle non-contiguous memory across region boundaries */
+ iov->iov_base = vring_map(&mr, desc->addr, desc->len,
+ desc->flags & VRING_DESC_F_WRITE);
+ if (!iov->iov_base) {
+ error_report("Failed to map descriptor addr %#" PRIx64 " len %u",
+ (uint64_t)desc->addr, desc->len);
+ return -EFAULT;
+ }
+
+ /* The MemoryRegion is looked up again and unref'ed later, leave the
+ * ref in place. */
+ iov->iov_len = desc->len;
+ *addr = desc->addr;
+ *num += 1;
+ return 0;
+}
+
/* This is stolen from linux/drivers/vhost/vhost.c. */
-static int get_indirect(Vring *vring,
- struct iovec iov[], struct iovec *iov_end,
- unsigned int *out_num, unsigned int *in_num,
+static int get_indirect(Vring *vring, VirtQueueElement *elem,
struct vring_desc *indirect)
{
struct vring_desc desc;
unsigned int i = 0, count, found = 0;
+ int ret;
/* Sanity check */
if (unlikely(indirect->len % sizeof(desc))) {
@@ -139,11 +229,12 @@ static int get_indirect(Vring *vring,
do {
struct vring_desc *desc_ptr;
+ MemoryRegion *mr;
/* Translate indirect descriptor */
- desc_ptr = hostmem_lookup(&vring->hostmem,
- indirect->addr + found * sizeof(desc),
- sizeof(desc), false);
+ desc_ptr = vring_map(&mr,
+ indirect->addr + found * sizeof(desc),
+ sizeof(desc), false);
if (!desc_ptr) {
error_report("Failed to map indirect descriptor "
"addr %#" PRIx64 " len %zu",
@@ -153,6 +244,7 @@ static int get_indirect(Vring *vring,
return -EFAULT;
}
desc = *desc_ptr;
+ memory_region_unref(mr);
/* Ensure descriptor has been loaded before accessing fields */
barrier(); /* read_barrier_depends(); */
@@ -170,42 +262,35 @@ static int get_indirect(Vring *vring,
return -EFAULT;
}
- /* Stop for now if there are not enough iovecs available. */
- if (iov >= iov_end) {
- return -ENOBUFS;
- }
-
- iov->iov_base = hostmem_lookup(&vring->hostmem, desc.addr, desc.len,
- desc.flags & VRING_DESC_F_WRITE);
- if (!iov->iov_base) {
- error_report("Failed to map indirect descriptor"
- "addr %#" PRIx64 " len %u",
- (uint64_t)desc.addr, desc.len);
- vring->broken = true;
- return -EFAULT;
- }
- iov->iov_len = desc.len;
- iov++;
-
- /* If this is an input descriptor, increment that count. */
- if (desc.flags & VRING_DESC_F_WRITE) {
- *in_num += 1;
- } else {
- /* If it's an output descriptor, they're all supposed
- * to come before any input descriptors. */
- if (unlikely(*in_num)) {
- error_report("Indirect descriptor "
- "has out after in: idx %u", i);
- vring->broken = true;
- return -EFAULT;
- }
- *out_num += 1;
+ ret = get_desc(vring, elem, &desc);
+ if (ret < 0) {
+ vring->broken |= (ret == -EFAULT);
+ return ret;
}
i = desc.next;
} while (desc.flags & VRING_DESC_F_NEXT);
return 0;
}
+void vring_free_element(VirtQueueElement *elem)
+{
+ int i;
+
+ /* This assumes that the iovecs, if changed, are never moved past
+ * the end of the valid area. This is true if iovec manipulations
+ * are done with iov_discard_front and iov_discard_back.
+ */
+ for (i = 0; i < elem->out_num; i++) {
+ vring_unmap(elem->out_sg[i].iov_base, false);
+ }
+
+ for (i = 0; i < elem->in_num; i++) {
+ vring_unmap(elem->in_sg[i].iov_base, true);
+ }
+
+ g_slice_free(VirtQueueElement, elem);
+}
+
/* This looks in the virtqueue and for the first available buffer, and converts
* it to an iovec for convenient access. Since descriptors consist of some
* number of output then some number of input descriptors, it's actually two
@@ -218,16 +303,18 @@ static int get_indirect(Vring *vring,
* Stolen from linux/drivers/vhost/vhost.c.
*/
int vring_pop(VirtIODevice *vdev, Vring *vring,
- struct iovec iov[], struct iovec *iov_end,
- unsigned int *out_num, unsigned int *in_num)
+ VirtQueueElement **p_elem)
{
struct vring_desc desc;
unsigned int i, head, found = 0, num = vring->vr.num;
uint16_t avail_idx, last_avail_idx;
+ VirtQueueElement *elem = NULL;
+ int ret;
/* If there was a fatal error then refuse operation */
if (vring->broken) {
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
/* Check it isn't doing very strange things with descriptor numbers. */
@@ -238,13 +325,14 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) {
error_report("Guest moved used index from %u to %u",
last_avail_idx, avail_idx);
- vring->broken = true;
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
/* If there's nothing new since last we looked. */
if (avail_idx == last_avail_idx) {
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto out;
}
/* Only get avail ring entries after they have been exposed by guest. */
@@ -254,32 +342,33 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
* the index we've seen. */
head = vring->vr.avail->ring[last_avail_idx % num];
+ elem = g_slice_new(VirtQueueElement);
+ elem->index = head;
+ elem->in_num = elem->out_num = 0;
+
/* If their number is silly, that's an error. */
if (unlikely(head >= num)) {
error_report("Guest says index %u > %u is available", head, num);
- vring->broken = true;
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
vring_avail_event(&vring->vr) = vring->vr.avail->idx;
}
- /* When we start there are none of either input nor output. */
- *out_num = *in_num = 0;
-
i = head;
do {
if (unlikely(i >= num)) {
error_report("Desc index is %u > %u, head = %u", i, num, head);
- vring->broken = true;
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
if (unlikely(++found > num)) {
error_report("Loop detected: last one at %u vq size %u head %u",
i, num, head);
- vring->broken = true;
- return -EFAULT;
+ ret = -EFAULT;
+ goto out;
}
desc = vring->vr.desc[i];
@@ -287,64 +376,50 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
barrier();
if (desc.flags & VRING_DESC_F_INDIRECT) {
- int ret = get_indirect(vring, iov, iov_end, out_num, in_num, &desc);
+ ret = get_indirect(vring, elem, &desc);
if (ret < 0) {
- return ret;
+ goto out;
}
continue;
}
- /* If there are not enough iovecs left, stop for now. The caller
- * should check if there are more descs available once they have dealt
- * with the current set.
- */
- if (iov >= iov_end) {
- return -ENOBUFS;
+ ret = get_desc(vring, elem, &desc);
+ if (ret < 0) {
+ goto out;
}
- /* TODO handle non-contiguous memory across region boundaries */
- iov->iov_base = hostmem_lookup(&vring->hostmem, desc.addr, desc.len,
- desc.flags & VRING_DESC_F_WRITE);
- if (!iov->iov_base) {
- error_report("Failed to map vring desc addr %#" PRIx64 " len %u",
- (uint64_t)desc.addr, desc.len);
- vring->broken = true;
- return -EFAULT;
- }
- iov->iov_len = desc.len;
- iov++;
-
- if (desc.flags & VRING_DESC_F_WRITE) {
- /* If this is an input descriptor,
- * increment that count. */
- *in_num += 1;
- } else {
- /* If it's an output descriptor, they're all supposed
- * to come before any input descriptors. */
- if (unlikely(*in_num)) {
- error_report("Descriptor has out after in: idx %d", i);
- vring->broken = true;
- return -EFAULT;
- }
- *out_num += 1;
- }
i = desc.next;
} while (desc.flags & VRING_DESC_F_NEXT);
/* On success, increment avail index. */
vring->last_avail_idx++;
+ *p_elem = elem;
return head;
+
+out:
+ assert(ret < 0);
+ if (ret == -EFAULT) {
+ vring->broken = true;
+ }
+ if (elem) {
+ vring_free_element(elem);
+ }
+ *p_elem = NULL;
+ return ret;
}
/* After we've used one of their buffers, we tell them about it.
*
* Stolen from linux/drivers/vhost/vhost.c.
*/
-void vring_push(Vring *vring, unsigned int head, int len)
+void vring_push(Vring *vring, VirtQueueElement *elem, int len)
{
struct vring_used_elem *used;
+ unsigned int head = elem->index;
uint16_t new;
+ vring_free_element(elem);
+
/* Don't touch vring if a fatal error occurred */
if (vring->broken) {
return;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 9504877120..d9754dbd33 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -337,10 +337,10 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static int virtio_balloon_device_init(VirtIODevice *vdev)
+static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
{
- DeviceState *qdev = DEVICE(vdev);
- VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOBalloon *s = VIRTIO_BALLOON(dev);
int ret;
virtio_init(vdev, "virtio-balloon", VIRTIO_ID_BALLOON, 8);
@@ -349,37 +349,36 @@ static int virtio_balloon_device_init(VirtIODevice *vdev)
virtio_balloon_stat, s);
if (ret < 0) {
- virtio_cleanup(VIRTIO_DEVICE(s));
- return -1;
+ error_setg(errp, "Adding balloon handler failed");
+ virtio_cleanup(vdev);
+ return;
}
s->ivq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output);
s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output);
s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats);
- register_savevm(qdev, "virtio-balloon", -1, 1,
+ register_savevm(dev, "virtio-balloon", -1, 1,
virtio_balloon_save, virtio_balloon_load, s);
- object_property_add(OBJECT(qdev), "guest-stats", "guest statistics",
+ object_property_add(OBJECT(dev), "guest-stats", "guest statistics",
balloon_stats_get_all, NULL, NULL, s, NULL);
- object_property_add(OBJECT(qdev), "guest-stats-polling-interval", "int",
+ object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int",
balloon_stats_get_poll_interval,
balloon_stats_set_poll_interval,
NULL, s, NULL);
- return 0;
}
-static int virtio_balloon_device_exit(DeviceState *qdev)
+static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIOBalloon *s = VIRTIO_BALLOON(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOBalloon *s = VIRTIO_BALLOON(dev);
balloon_stats_destroy_timer(s);
qemu_remove_balloon_handler(s);
- unregister_savevm(qdev, "virtio-balloon", s);
+ unregister_savevm(dev, "virtio-balloon", s);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_balloon_properties[] = {
@@ -390,10 +389,11 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_balloon_device_exit;
+
dc->props = virtio_balloon_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- vdc->init = virtio_balloon_device_init;
+ vdc->realize = virtio_balloon_device_realize;
+ vdc->unrealize = virtio_balloon_device_unrealize;
vdc->get_config = virtio_balloon_get_config;
vdc->set_config = virtio_balloon_set_config;
vdc->get_features = virtio_balloon_get_features;
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index e6b103c991..eb77019267 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -37,8 +37,8 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
#define DPRINTF(fmt, ...) do { } while (0)
#endif
-/* Plug the VirtIODevice */
-int virtio_bus_plug_device(VirtIODevice *vdev)
+/* A VirtIODevice is being plugged */
+int virtio_bus_device_plugged(VirtIODevice *vdev)
{
DeviceState *qdev = DEVICE(vdev);
BusState *qbus = BUS(qdev_get_parent_bus(qdev));
@@ -46,8 +46,6 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
DPRINTF("%s: plug device.\n", qbus->name);
- bus->vdev = vdev;
-
if (klass->device_plugged != NULL) {
klass->device_plugged(qbus->parent);
}
@@ -58,73 +56,83 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
/* Reset the virtio_bus */
void virtio_bus_reset(VirtioBusState *bus)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+
DPRINTF("%s: reset device.\n", qbus->name);
- if (bus->vdev != NULL) {
- virtio_reset(bus->vdev);
+ if (vdev != NULL) {
+ virtio_reset(vdev);
}
}
-/* Destroy the VirtIODevice */
-void virtio_bus_destroy_device(VirtioBusState *bus)
+/* A VirtIODevice is being unplugged */
+void virtio_bus_device_unplugged(VirtIODevice *vdev)
{
- BusState *qbus = BUS(bus);
- VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+ DeviceState *qdev = DEVICE(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(qdev));
+ VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus);
+
DPRINTF("%s: remove device.\n", qbus->name);
- if (bus->vdev != NULL) {
- if (klass->device_unplug != NULL) {
- klass->device_unplug(qbus->parent);
+ if (vdev != NULL) {
+ if (klass->device_unplugged != NULL) {
+ klass->device_unplugged(qbus->parent);
}
- object_unparent(OBJECT(bus->vdev));
- bus->vdev = NULL;
}
}
/* Get the device id of the plugged device. */
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
{
- assert(bus->vdev != NULL);
- return bus->vdev->device_id;
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ assert(vdev != NULL);
+ return vdev->device_id;
}
/* Get the config_len field of the plugged device. */
size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
{
- assert(bus->vdev != NULL);
- return bus->vdev->config_len;
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
+ assert(vdev != NULL);
+ return vdev->config_len;
}
/* Get the features of the plugged device. */
uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
uint32_t requested_features)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
assert(k->get_features != NULL);
- return k->get_features(bus->vdev, requested_features);
+ return k->get_features(vdev, requested_features);
}
/* Set the features of the plugged device. */
void virtio_bus_set_vdev_features(VirtioBusState *bus,
uint32_t requested_features)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->set_features != NULL) {
- k->set_features(bus->vdev, requested_features);
+ k->set_features(vdev, requested_features);
}
}
/* Get bad features of the plugged device. */
uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->bad_features != NULL) {
- return k->bad_features(bus->vdev);
+ return k->bad_features(vdev);
} else {
return 0;
}
@@ -133,22 +141,26 @@ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
/* Get config of the plugged device. */
void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->get_config != NULL) {
- k->get_config(bus->vdev, config);
+ k->get_config(vdev, config);
}
}
/* Set config of the plugged device. */
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
{
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
VirtioDeviceClass *k;
- assert(bus->vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+
+ assert(vdev != NULL);
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
if (k->set_config != NULL) {
- k->set_config(bus->vdev, config);
+ k->set_config(vdev, config);
}
}
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 29cf284d12..8829eb0e26 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -95,7 +95,7 @@ static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size,
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
- VirtIODevice *vdev = proxy->bus.vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset);
@@ -185,7 +185,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
- VirtIODevice *vdev = proxy->bus.vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n",
(int)offset, value);
@@ -298,12 +298,13 @@ static const MemoryRegionOps virtio_mem_ops = {
static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int level;
- if (!proxy->bus.vdev) {
+ if (!vdev) {
return;
}
- level = (proxy->bus.vdev->isr != 0);
+ level = (vdev->isr != 0);
DPRINTF("virtio_mmio setting IRQ %d\n", level);
qemu_set_irq(proxy->irq, level);
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 7647be8a3c..30c9f2b698 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -113,31 +113,40 @@ static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d)
static void virtio_pci_notify(DeviceState *d, uint16_t vector)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
+
if (msix_enabled(&proxy->pci_dev))
msix_notify(&proxy->pci_dev, vector);
- else
- pci_set_irq(&proxy->pci_dev, proxy->vdev->isr & 1);
+ else {
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ pci_set_irq(&proxy->pci_dev, vdev->isr & 1);
+ }
}
static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
pci_device_save(&proxy->pci_dev, f);
msix_save(&proxy->pci_dev, f);
if (msix_present(&proxy->pci_dev))
- qemu_put_be16(f, proxy->vdev->config_vector);
+ qemu_put_be16(f, vdev->config_vector);
}
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
if (msix_present(&proxy->pci_dev))
- qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
+ qemu_put_be16(f, virtio_queue_vector(vdev, n));
}
static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
int ret;
ret = pci_device_load(&proxy->pci_dev, f);
if (ret) {
@@ -146,12 +155,12 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
msix_unuse_all_vectors(&proxy->pci_dev);
msix_load(&proxy->pci_dev, f);
if (msix_present(&proxy->pci_dev)) {
- qemu_get_be16s(f, &proxy->vdev->config_vector);
+ qemu_get_be16s(f, &vdev->config_vector);
} else {
- proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
+ vdev->config_vector = VIRTIO_NO_VECTOR;
}
- if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
- return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
+ if (vdev->config_vector != VIRTIO_NO_VECTOR) {
+ return msix_vector_use(&proxy->pci_dev, vdev->config_vector);
}
return 0;
}
@@ -159,13 +168,15 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
uint16_t vector;
if (msix_present(&proxy->pci_dev)) {
qemu_get_be16s(f, &vector);
} else {
vector = VIRTIO_NO_VECTOR;
}
- virtio_queue_set_vector(proxy->vdev, n, vector);
+ virtio_queue_set_vector(vdev, n, vector);
if (vector != VIRTIO_NO_VECTOR) {
return msix_vector_use(&proxy->pci_dev, vector);
}
@@ -175,7 +186,8 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
int n, bool assign, bool set_handler)
{
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
int r = 0;
@@ -200,6 +212,7 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int n, r;
if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
@@ -209,7 +222,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
}
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -223,7 +236,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
assign_error:
while (--n >= 0) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -236,6 +249,7 @@ assign_error:
static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int r;
int n;
@@ -244,7 +258,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
}
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
- if (!virtio_queue_get_num(proxy->vdev, n)) {
+ if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -257,7 +271,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
VirtIOPCIProxy *proxy = opaque;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
hwaddr pa;
switch (addr) {
@@ -272,7 +286,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
if (pa == 0) {
virtio_pci_stop_ioeventfd(proxy);
- virtio_reset(proxy->vdev);
+ virtio_reset(vdev);
msix_unuse_all_vectors(&proxy->pci_dev);
}
else
@@ -299,7 +313,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
if (vdev->status == 0) {
- virtio_reset(proxy->vdev);
+ virtio_reset(vdev);
msix_unuse_all_vectors(&proxy->pci_dev);
}
@@ -335,7 +349,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
{
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint32_t ret = 0xFFFFFFFF;
switch (addr) {
@@ -381,6 +395,7 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
unsigned size)
{
VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
uint64_t val = 0;
if (addr < config) {
@@ -390,16 +405,16 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
switch (size) {
case 1:
- val = virtio_config_readb(proxy->vdev, addr);
+ val = virtio_config_readb(vdev, addr);
break;
case 2:
- val = virtio_config_readw(proxy->vdev, addr);
+ val = virtio_config_readw(vdev, addr);
if (virtio_is_big_endian()) {
val = bswap16(val);
}
break;
case 4:
- val = virtio_config_readl(proxy->vdev, addr);
+ val = virtio_config_readl(vdev, addr);
if (virtio_is_big_endian()) {
val = bswap32(val);
}
@@ -413,6 +428,7 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
{
VirtIOPCIProxy *proxy = opaque;
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (addr < config) {
virtio_ioport_write(proxy, addr, val);
return;
@@ -424,19 +440,19 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
*/
switch (size) {
case 1:
- virtio_config_writeb(proxy->vdev, addr, val);
+ virtio_config_writeb(vdev, addr, val);
break;
case 2:
if (virtio_is_big_endian()) {
val = bswap16(val);
}
- virtio_config_writew(proxy->vdev, addr, val);
+ virtio_config_writew(vdev, addr, val);
break;
case 4:
if (virtio_is_big_endian()) {
val = bswap32(val);
}
- virtio_config_writel(proxy->vdev, addr, val);
+ virtio_config_writel(vdev, addr, val);
break;
}
}
@@ -455,6 +471,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
uint32_t val, int len)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
pci_default_write_config(pci_dev, address, val, len);
@@ -462,8 +479,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) &&
!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
virtio_pci_stop_ioeventfd(proxy);
- virtio_set_status(proxy->vdev,
- proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
+ virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
}
}
@@ -506,7 +522,8 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
unsigned int vector)
{
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
int ret;
ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, NULL, irqfd->virq);
@@ -517,7 +534,8 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector)
{
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
int ret;
@@ -529,7 +547,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
{
PCIDevice *dev = &proxy->pci_dev;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
unsigned int vector;
int ret, queue_no;
@@ -578,7 +596,7 @@ undo:
static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
{
PCIDevice *dev = &proxy->pci_dev;
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
unsigned int vector;
int queue_no;
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
@@ -606,8 +624,9 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
unsigned int vector,
MSIMessage msg)
{
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd;
int ret = 0;
@@ -626,10 +645,10 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
* Otherwise, set it up now.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(proxy->vdev, queue_no, false);
+ k->guest_notifier_mask(vdev, queue_no, false);
/* Test after unmasking to avoid losing events. */
if (k->guest_notifier_pending &&
- k->guest_notifier_pending(proxy->vdev, queue_no)) {
+ k->guest_notifier_pending(vdev, queue_no)) {
event_notifier_set(n);
}
} else {
@@ -642,13 +661,14 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector)
{
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
/* If guest supports masking, keep irqfd but mask it.
* Otherwise, clean it up now.
*/
if (k->guest_notifier_mask) {
- k->guest_notifier_mask(proxy->vdev, queue_no, true);
+ k->guest_notifier_mask(vdev, queue_no, true);
} else {
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
}
@@ -658,7 +678,7 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
MSIMessage msg)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int ret, queue_no;
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
@@ -688,7 +708,7 @@ undo:
static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
int queue_no;
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
@@ -707,7 +727,7 @@ static void virtio_pci_vector_poll(PCIDevice *dev,
unsigned int vector_end)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
int queue_no;
unsigned int vector;
@@ -739,8 +759,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
bool with_irqfd)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
if (assign) {
@@ -755,7 +776,7 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
}
if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) {
- vdc->guest_notifier_mask(proxy->vdev, n, !assign);
+ vdc->guest_notifier_mask(vdev, n, !assign);
}
return 0;
@@ -770,7 +791,7 @@ static bool virtio_pci_query_guest_notifiers(DeviceState *d)
static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- VirtIODevice *vdev = proxy->vdev;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
int r, n;
bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
@@ -864,11 +885,12 @@ static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
static void virtio_pci_vmstate_change(DeviceState *d, bool running)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
if (running) {
/* Try to find out if the guest has bus master disabled, but is
in ready state. Then we have a buggy guest OS. */
- if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
!(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
}
@@ -943,8 +965,6 @@ static void virtio_pci_device_plugged(DeviceState *d)
uint8_t *config;
uint32_t size;
- proxy->vdev = bus->vdev;
-
config = proxy->pci_dev.config;
if (proxy->class_code) {
pci_config_set_class(config, proxy->class_code);
@@ -982,6 +1002,15 @@ static void virtio_pci_device_plugged(DeviceState *d)
proxy->host_features);
}
+static void virtio_pci_device_unplugged(DeviceState *d)
+{
+ PCIDevice *pci_dev = PCI_DEVICE(d);
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
+
+ virtio_pci_stop_ioeventfd(proxy);
+ msix_uninit_exclusive_bar(pci_dev);
+}
+
static int virtio_pci_init(PCIDevice *pci_dev)
{
VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
@@ -996,9 +1025,7 @@ static int virtio_pci_init(PCIDevice *pci_dev)
static void virtio_pci_exit(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
- virtio_pci_stop_ioeventfd(proxy);
memory_region_destroy(&proxy->bar);
- msix_uninit_exclusive_bar(pci_dev);
}
static void virtio_pci_reset(DeviceState *qdev)
@@ -1533,6 +1560,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
k->vmstate_change = virtio_pci_vmstate_change;
k->device_plugged = virtio_pci_device_plugged;
+ k->device_unplugged = virtio_pci_device_unplugged;
}
static const TypeInfo virtio_pci_bus_info = {
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 917bcc5348..dc332ae774 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -82,7 +82,6 @@ typedef struct VirtioPCIClass {
struct VirtIOPCIProxy {
PCIDevice pci_dev;
- VirtIODevice *vdev;
MemoryRegion bar;
uint32_t flags;
uint32_t class_code;
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index b22ccf1008..755fdee628 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -133,27 +133,27 @@ static void check_rate_limit(void *opaque)
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
}
-static int virtio_rng_device_init(VirtIODevice *vdev)
+static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
{
- DeviceState *qdev = DEVICE(vdev);
- VirtIORNG *vrng = VIRTIO_RNG(vdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIORNG *vrng = VIRTIO_RNG(dev);
Error *local_err = NULL;
if (!vrng->conf.period_ms > 0) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "period",
- "a positive number");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "period",
+ "a positive number");
+ return;
}
if (vrng->conf.rng == NULL) {
vrng->conf.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM));
- object_property_add_child(OBJECT(qdev),
+ object_property_add_child(OBJECT(dev),
"default-backend",
OBJECT(vrng->conf.default_backend),
NULL);
- object_property_set_link(OBJECT(qdev),
+ object_property_set_link(OBJECT(dev),
OBJECT(vrng->conf.default_backend),
"rng", NULL);
}
@@ -162,15 +162,14 @@ static int virtio_rng_device_init(VirtIODevice *vdev)
vrng->rng = vrng->conf.rng;
if (vrng->rng == NULL) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
+ return;
}
rng_backend_open(vrng->rng, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
- return -1;
+ error_propagate(errp, local_err);
+ return;
}
vrng->vq = virtio_add_queue(vdev, 8, handle_input);
@@ -184,22 +183,19 @@ static int virtio_rng_device_init(VirtIODevice *vdev)
timer_mod(vrng->rate_limit_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
- register_savevm(qdev, "virtio-rng", -1, 1, virtio_rng_save,
+ register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
virtio_rng_load, vrng);
-
- return 0;
}
-static int virtio_rng_device_exit(DeviceState *qdev)
+static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIORNG *vrng = VIRTIO_RNG(qdev);
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIORNG *vrng = VIRTIO_RNG(dev);
timer_del(vrng->rate_limit_timer);
timer_free(vrng->rate_limit_timer);
- unregister_savevm(qdev, "virtio-rng", vrng);
+ unregister_savevm(dev, "virtio-rng", vrng);
virtio_cleanup(vdev);
- return 0;
}
static Property virtio_rng_properties[] = {
@@ -211,10 +207,11 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
- dc->exit = virtio_rng_device_exit;
+
dc->props = virtio_rng_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- vdc->init = virtio_rng_device_init;
+ vdc->realize = virtio_rng_device_realize;
+ vdc->unrealize = virtio_rng_device_unrealize;
vdc->get_features = get_features;
}
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 2f1e73bc75..a001e668c4 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1150,35 +1150,51 @@ void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name)
}
}
-static int virtio_device_init(DeviceState *qdev)
+static void virtio_device_realize(DeviceState *dev, Error **errp)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev);
- assert(k->init != NULL);
- if (k->init(vdev) < 0) {
- return -1;
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
+ Error *err = NULL;
+
+ if (vdc->realize != NULL) {
+ vdc->realize(dev, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
}
- virtio_bus_plug_device(vdev);
- return 0;
+ virtio_bus_device_plugged(vdev);
}
-static int virtio_device_exit(DeviceState *qdev)
+static void virtio_device_unrealize(DeviceState *dev, Error **errp)
{
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev);
+ Error *err = NULL;
+
+ virtio_bus_device_unplugged(vdev);
+
+ if (vdc->unrealize != NULL) {
+ vdc->unrealize(dev, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
if (vdev->bus_name) {
g_free(vdev->bus_name);
vdev->bus_name = NULL;
}
- return 0;
}
static void virtio_device_class_init(ObjectClass *klass, void *data)
{
/* Set the default value here. */
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->init = virtio_device_init;
- dc->exit = virtio_device_exit;
+
+ dc->realize = virtio_device_realize;
+ dc->unrealize = virtio_device_unrealize;
dc->bus_type = TYPE_VIRTIO_BUS;
}
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
index 387962ec4a..f28161b2d6 100644
--- a/hw/watchdog/watchdog.c
+++ b/hw/watchdog/watchdog.c
@@ -66,7 +66,8 @@ int select_watchdog(const char *p)
QLIST_FOREACH(model, &watchdog_list, entry) {
if (strcasecmp(model->wdt_name, p) == 0) {
/* add the device */
- opts = qemu_opts_create_nofail(qemu_find_opts("device"));
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ &error_abort);
qemu_opt_set(opts, "driver", p);
return 0;
}
diff --git a/hw/xen/xen_apic.c b/hw/xen/xen_apic.c
index 9f91e0f0c9..63bb7f77c6 100644
--- a/hw/xen/xen_apic.c
+++ b/hw/xen/xen_apic.c
@@ -36,8 +36,10 @@ static const MemoryRegionOps xen_apic_io_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void xen_apic_init(APICCommonState *s)
+static void xen_apic_realize(DeviceState *dev, Error **errp)
{
+ APICCommonState *s = APIC_COMMON(dev);
+
memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s,
"xen-apic-msi", APIC_SPACE_SIZE);
@@ -72,7 +74,7 @@ static void xen_apic_class_init(ObjectClass *klass, void *data)
{
APICCommonClass *k = APIC_COMMON_CLASS(klass);
- k->init = xen_apic_init;
+ k->realize = xen_apic_realize;
k->set_base = xen_apic_set_base;
k->set_tpr = xen_apic_set_tpr;
k->get_tpr = xen_apic_get_tpr;
diff --git a/include/block/block.h b/include/block/block.h
index 36efaeac2d..963a61fa4c 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -184,7 +184,11 @@ 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_file_open(BlockDriverState **pbs, const char *filename,
- QDict *options, int flags, Error **errp);
+ const char *reference, QDict *options, int flags,
+ Error **errp);
+int bdrv_open_image(BlockDriverState **pbs, const char *filename,
+ QDict *options, const char *bdref_key, int flags,
+ bool force_raw, bool allow_none, Error **errp);
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
int flags, BlockDriver *drv, Error **errp);
@@ -220,7 +224,6 @@ BlockDriverAIOCB *bdrv_aio_write_zeroes(BlockDriverState *bs, int64_t sector_num
int nb_sectors, BdrvRequestFlags flags,
BlockDriverCompletionFunc *cb, void *opaque);
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
-int bdrv_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov);
int bdrv_pread(BlockDriverState *bs, int64_t offset,
void *buf, int count);
int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
@@ -249,6 +252,7 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset);
int64_t bdrv_getlength(BlockDriverState *bs);
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
+int bdrv_refresh_limits(BlockDriverState *bs);
int bdrv_commit(BlockDriverState *bs);
int bdrv_commit_all(void);
int bdrv_change_backing_file(BlockDriverState *bs,
@@ -283,16 +287,16 @@ int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
/* external snapshots */
typedef enum {
- EXT_SNAPSHOT_ALLOWED,
- EXT_SNAPSHOT_FORBIDDEN,
-} ExtSnapshotPerm;
+ BS_IS_A_FILTER,
+ BS_FILTER_PASS_DOWN,
+ BS_AUTHORIZATION_COUNT,
+} BsAuthorization;
-/* return EXT_SNAPSHOT_ALLOWED if external snapshot is allowed
- * return EXT_SNAPSHOT_FORBIDDEN if external snapshot is forbidden
- */
-ExtSnapshotPerm bdrv_check_ext_snapshot(BlockDriverState *bs);
-/* helper used to forbid external snapshots like in blkverify */
-ExtSnapshotPerm bdrv_check_ext_snapshot_forbidden(BlockDriverState *bs);
+bool bdrv_generic_is_first_non_filter(BlockDriverState *bs,
+ BlockDriverState *candidate);
+bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs,
+ BlockDriverState *candidate);
+bool bdrv_is_first_non_filter(BlockDriverState *candidate);
/* async block I/O */
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
@@ -374,6 +378,11 @@ void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
const char *bdrv_get_format_name(BlockDriverState *bs);
BlockDriverState *bdrv_find(const char *name);
+BlockDriverState *bdrv_find_node(const char *node_name);
+BlockDeviceInfoList *bdrv_named_nodes_list(void);
+BlockDriverState *bdrv_lookup_bs(const char *device,
+ const char *node_name,
+ Error **errp);
BlockDriverState *bdrv_next(BlockDriverState *bs);
void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
void *opaque);
@@ -418,7 +427,10 @@ void bdrv_img_create(const char *filename, const char *fmt,
char *options, uint64_t img_size, int flags,
Error **errp, bool quiet);
-void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
+/* Returns the alignment in bytes that is required so that no bounce buffer
+ * is required throughout the stack */
+size_t bdrv_opt_mem_align(BlockDriverState *bs);
+void bdrv_set_guest_block_size(BlockDriverState *bs, int align);
void *qemu_blockalign(BlockDriverState *bs, size_t size);
bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov);
@@ -515,6 +527,14 @@ typedef enum {
BLKDBG_FLUSH_TO_OS,
BLKDBG_FLUSH_TO_DISK,
+ BLKDBG_PWRITEV_RMW_HEAD,
+ BLKDBG_PWRITEV_RMW_AFTER_HEAD,
+ BLKDBG_PWRITEV_RMW_TAIL,
+ BLKDBG_PWRITEV_RMW_AFTER_TAIL,
+ BLKDBG_PWRITEV,
+ BLKDBG_PWRITEV_ZERO,
+ BLKDBG_PWRITEV_DONE,
+
BLKDBG_EVENT_MAX,
} BlkDebugEvent;
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8b132d7178..0bcf1c9b8c 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -57,22 +57,35 @@
typedef struct BdrvTrackedRequest {
BlockDriverState *bs;
- int64_t sector_num;
- int nb_sectors;
+ int64_t offset;
+ unsigned int bytes;
bool is_write;
+
+ bool serialising;
+ int64_t overlap_offset;
+ unsigned int overlap_bytes;
+
QLIST_ENTRY(BdrvTrackedRequest) list;
Coroutine *co; /* owner, used for deadlock detection */
CoQueue wait_queue; /* coroutines blocked on this request */
+
+ struct BdrvTrackedRequest *waiting_for;
} BdrvTrackedRequest;
struct BlockDriver {
const char *format_name;
int instance_size;
- /* if not defined external snapshots are allowed
- * future block filters will query their children to build the response
+ /* this table of boolean contains authorizations for the block operations */
+ bool authorizations[BS_AUTHORIZATION_COUNT];
+ /* for snapshots complex block filter like Quorum can implement the
+ * following recursive callback instead of BS_IS_A_FILTER.
+ * It's purpose is to recurse on the filter children while calling
+ * bdrv_recurse_is_first_non_filter on them.
+ * For a sample implementation look in the future Quorum block filter.
*/
- ExtSnapshotPerm (*bdrv_check_ext_snapshot)(BlockDriverState *bs);
+ bool (*bdrv_recurse_is_first_non_filter)(BlockDriverState *bs,
+ BlockDriverState *candidate);
int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
int (*bdrv_probe_device)(const char *filename);
@@ -226,6 +239,8 @@ struct BlockDriver {
int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag);
bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag);
+ int (*bdrv_refresh_limits)(BlockDriverState *bs);
+
/*
* Returns 1 if newly created images are guaranteed to contain only
* zeros, 0 otherwise.
@@ -250,6 +265,9 @@ typedef struct BlockLimits {
/* optimal transfer length in sectors */
int opt_transfer_length;
+
+ /* memory alignment so that no bounce buffer is needed */
+ size_t opt_mem_alignment;
} BlockLimits;
/*
@@ -291,8 +309,8 @@ struct BlockDriverState {
/* Callback before write request is processed */
NotifierWithReturnList before_write_notifiers;
- /* number of in-flight copy-on-read requests */
- unsigned int copy_on_read_in_flight;
+ /* number of in-flight serialising requests */
+ unsigned int serialising_in_flight;
/* I/O throttling */
ThrottleState throttle_state;
@@ -314,8 +332,11 @@ struct BlockDriverState {
/* Whether produces zeros when read beyond eof */
bool zero_beyond_eof;
- /* the memory alignment required for the buffers handled by this driver */
- int buffer_alignment;
+ /* Alignment requirement for offset/length of I/O requests */
+ unsigned int request_alignment;
+
+ /* the block size for which the guest device expects atomicity */
+ int guest_block_size;
/* do we need to tell the quest if we have a volatile write cache? */
int enable_write_cache;
@@ -325,11 +346,18 @@ struct BlockDriverState {
BlockdevOnError on_read_error, on_write_error;
bool iostatus_enabled;
BlockDeviceIoStatus iostatus;
+
+ /* the following member gives a name to every node on the bs graph. */
+ char node_name[32];
+ /* element of the list of named nodes building the graph */
+ QTAILQ_ENTRY(BlockDriverState) node_list;
+ /* Device name is the name associated with the "drive" the guest sees */
char device_name[32];
+ /* element of the list of "drives" the guest sees */
+ QTAILQ_ENTRY(BlockDriverState) device_list;
QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
int refcnt;
int in_use; /* users other than guest access, eg. block migration */
- QTAILQ_ENTRY(BlockDriverState) list;
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
@@ -394,8 +422,9 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
/**
* commit_start:
- * @bs: Top Block device
- * @base: Block device that will be written into, and become the new top
+ * @bs: Active block device.
+ * @top: Top block device to be committed.
+ * @base: Block device that will be written into, and become the new top.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @on_error: The action to take upon error.
* @cb: Completion function for the job.
@@ -407,7 +436,22 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
BlockDriverState *top, int64_t speed,
BlockdevOnError on_error, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
-
+/**
+ * commit_active_start:
+ * @bs: Active block device to be committed.
+ * @base: Block device that will be written into, and become the new top.
+ * @speed: The maximum speed, in bytes per second, or 0 for unlimited.
+ * @on_error: The action to take upon error.
+ * @cb: Completion function for the job.
+ * @opaque: Opaque pointer value passed to @cb.
+ * @errp: Error object.
+ *
+ */
+void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
+ int64_t speed,
+ BlockdevOnError on_error,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, Error **errp);
/*
* mirror_start:
* @bs: Block device to operate on.
diff --git a/include/block/qapi.h b/include/block/qapi.h
index 9518ee4001..e92c00daf6 100644
--- a/include/block/qapi.h
+++ b/include/block/qapi.h
@@ -29,6 +29,7 @@
#include "block/block.h"
#include "block/snapshot.h"
+BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs);
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
SnapshotInfoList **p_list,
Error **errp);
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index b6998f055a..4cb4b4a53a 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -21,6 +21,7 @@
#include "qemu-common.h"
#include "exec/cpu-common.h"
+#include "exec/memory.h"
#include "qemu/thread.h"
#include "qom/cpu.h"
@@ -459,7 +460,7 @@ typedef struct RAMBlock {
typedef struct RAMList {
QemuMutex mutex;
/* Protected by the iothread lock. */
- uint8_t *phys_dirty;
+ unsigned long *dirty_memory[DIRTY_MEMORY_NUM];
RAMBlock *mru_block;
/* Protected by the ramlist lock. */
QTAILQ_HEAD(, RAMBlock) blocks;
diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h
index d0e063392a..25c43c06e9 100644
--- a/include/exec/memory-internal.h
+++ b/include/exec/memory-internal.h
@@ -20,9 +20,6 @@
#define MEMORY_INTERNAL_H
#ifndef CONFIG_USER_ONLY
-#include "hw/xen/xen.h"
-
-
typedef struct AddressSpaceDispatch AddressSpaceDispatch;
void address_space_init_dispatch(AddressSpace *as);
@@ -33,92 +30,5 @@ extern const MemoryRegionOps unassigned_mem_ops;
bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr,
unsigned size, bool is_write);
-ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
- MemoryRegion *mr);
-ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
-void *qemu_get_ram_ptr(ram_addr_t addr);
-void qemu_ram_free(ram_addr_t addr);
-void qemu_ram_free_from_ptr(ram_addr_t addr);
-
-#define VGA_DIRTY_FLAG 0x01
-#define CODE_DIRTY_FLAG 0x02
-#define MIGRATION_DIRTY_FLAG 0x08
-
-static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
-{
- return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
-}
-
-/* read dirty bit (return 0 or 1) */
-static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
-{
- return cpu_physical_memory_get_dirty_flags(addr) == 0xff;
-}
-
-static inline int cpu_physical_memory_get_dirty(ram_addr_t start,
- ram_addr_t length,
- int dirty_flags)
-{
- int ret = 0;
- ram_addr_t addr, end;
-
- end = TARGET_PAGE_ALIGN(start + length);
- start &= TARGET_PAGE_MASK;
- for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
- ret |= cpu_physical_memory_get_dirty_flags(addr) & dirty_flags;
- }
- return ret;
-}
-
-static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
- int dirty_flags)
-{
- return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
-}
-
-static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
-{
- cpu_physical_memory_set_dirty_flags(addr, 0xff);
-}
-
-static inline int cpu_physical_memory_clear_dirty_flags(ram_addr_t addr,
- int dirty_flags)
-{
- int mask = ~dirty_flags;
-
- return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] &= mask;
-}
-
-static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
- ram_addr_t length,
- int dirty_flags)
-{
- ram_addr_t addr, end;
-
- end = TARGET_PAGE_ALIGN(start + length);
- start &= TARGET_PAGE_MASK;
- for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
- cpu_physical_memory_set_dirty_flags(addr, dirty_flags);
- }
- xen_modified_memory(addr, length);
-}
-
-static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
- ram_addr_t length,
- int dirty_flags)
-{
- ram_addr_t addr, end;
-
- end = TARGET_PAGE_ALIGN(start + length);
- start &= TARGET_PAGE_MASK;
- for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
- cpu_physical_memory_clear_dirty_flags(addr, dirty_flags);
- }
-}
-
-void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
- int dirty_flags);
-
#endif
-
#endif
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 480dfbf9da..296d6ab2f4 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -16,6 +16,11 @@
#ifndef CONFIG_USER_ONLY
+#define DIRTY_MEMORY_VGA 0
+#define DIRTY_MEMORY_CODE 1
+#define DIRTY_MEMORY_MIGRATION 2
+#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */
+
#include <stdint.h>
#include <stdbool.h>
#include "qemu-common.h"
@@ -33,13 +38,6 @@
typedef struct MemoryRegionOps MemoryRegionOps;
typedef struct MemoryRegionMmio MemoryRegionMmio;
-/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic
- * registration.
- */
-#define DIRTY_MEMORY_VGA 0
-#define DIRTY_MEMORY_CODE 1
-#define DIRTY_MEMORY_MIGRATION 3
-
struct MemoryRegionMmio {
CPUReadMemoryFunc *read[3];
CPUWriteMemoryFunc *write[3];
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
new file mode 100644
index 0000000000..33c8acc02e
--- /dev/null
+++ b/include/exec/ram_addr.h
@@ -0,0 +1,147 @@
+/*
+ * Declarations for cpu physical memory functions
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Avi Kivity <avi@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.
+ *
+ */
+
+/*
+ * This header is for use by exec.c and memory.c ONLY. Do not include it.
+ * The functions declared here will be removed soon.
+ */
+
+#ifndef RAM_ADDR_H
+#define RAM_ADDR_H
+
+#ifndef CONFIG_USER_ONLY
+#include "hw/xen/xen.h"
+
+ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
+ MemoryRegion *mr);
+ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
+void *qemu_get_ram_ptr(ram_addr_t addr);
+void qemu_ram_free(ram_addr_t addr);
+void qemu_ram_free_from_ptr(ram_addr_t addr);
+
+static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
+ ram_addr_t length,
+ unsigned client)
+{
+ unsigned long end, page, next;
+
+ assert(client < DIRTY_MEMORY_NUM);
+
+ end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
+ page = start >> TARGET_PAGE_BITS;
+ next = find_next_bit(ram_list.dirty_memory[client], end, page);
+
+ return next < end;
+}
+
+static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr,
+ unsigned client)
+{
+ return cpu_physical_memory_get_dirty(addr, 1, client);
+}
+
+static inline bool cpu_physical_memory_is_clean(ram_addr_t addr)
+{
+ bool vga = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_VGA);
+ bool code = cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_CODE);
+ bool migration =
+ cpu_physical_memory_get_dirty_flag(addr, DIRTY_MEMORY_MIGRATION);
+ return !(vga && code && migration);
+}
+
+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(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);
+ bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page);
+ xen_modified_memory(start, length);
+}
+
+static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
+ ram_addr_t start,
+ ram_addr_t pages)
+{
+ unsigned long i, j;
+ unsigned long page_number, c;
+ hwaddr addr;
+ ram_addr_t ram_addr;
+ unsigned long len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS;
+ unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE;
+ 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) {
+ long k;
+ long nr = BITS_TO_LONGS(pages);
+
+ for (k = 0; k < nr; k++) {
+ if (bitmap[k]) {
+ unsigned long temp = leul_to_cpu(bitmap[k]);
+
+ 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;
+ }
+ }
+ xen_modified_memory(start, pages);
+ } else {
+ /*
+ * bitmap-traveling is faster than memory-traveling (for addr...)
+ * especially when most of the memory is not dirty.
+ */
+ for (i = 0; i < len; i++) {
+ if (bitmap[i] != 0) {
+ c = leul_to_cpu(bitmap[i]);
+ do {
+ j = ffsl(c) - 1;
+ c &= ~(1ul << j);
+ page_number = (i * HOST_LONG_BITS + j) * hpratio;
+ addr = page_number * TARGET_PAGE_SIZE;
+ ram_addr = start + addr;
+ cpu_physical_memory_set_dirty_range(ram_addr,
+ TARGET_PAGE_SIZE * hpratio);
+ } while (c != 0);
+ }
+ }
+ }
+}
+
+static inline void cpu_physical_memory_clear_dirty_range(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);
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
+ unsigned client);
+
+#endif
+#endif
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 2365274daa..806ae13780 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -152,7 +152,8 @@ enum {
float_round_nearest_even = 0,
float_round_down = 1,
float_round_up = 2,
- float_round_to_zero = 3
+ float_round_to_zero = 3,
+ float_round_ties_away = 4,
};
/*----------------------------------------------------------------------------
@@ -180,12 +181,22 @@ typedef struct float_status {
flag default_nan_mode;
} float_status;
-void set_float_rounding_mode(int val STATUS_PARAM);
-void set_float_exception_flags(int val STATUS_PARAM);
INLINE void set_float_detect_tininess(int val STATUS_PARAM)
{
STATUS(float_detect_tininess) = val;
}
+INLINE void set_float_rounding_mode(int val STATUS_PARAM)
+{
+ STATUS(float_rounding_mode) = val;
+}
+INLINE void set_float_exception_flags(int val STATUS_PARAM)
+{
+ STATUS(float_exception_flags) = val;
+}
+INLINE void set_floatx80_rounding_precision(int val STATUS_PARAM)
+{
+ STATUS(floatx80_rounding_precision) = val;
+}
INLINE void set_flush_to_zero(flag val STATUS_PARAM)
{
STATUS(flush_to_zero) = val;
@@ -198,11 +209,34 @@ INLINE void set_default_nan_mode(flag val STATUS_PARAM)
{
STATUS(default_nan_mode) = val;
}
+INLINE int get_float_detect_tininess(float_status *status)
+{
+ return STATUS(float_detect_tininess);
+}
+INLINE int get_float_rounding_mode(float_status *status)
+{
+ return STATUS(float_rounding_mode);
+}
INLINE int get_float_exception_flags(float_status *status)
{
return STATUS(float_exception_flags);
}
-void set_floatx80_rounding_precision(int val STATUS_PARAM);
+INLINE int get_floatx80_rounding_precision(float_status *status)
+{
+ return STATUS(floatx80_rounding_precision);
+}
+INLINE flag get_flush_to_zero(float_status *status)
+{
+ return STATUS(flush_to_zero);
+}
+INLINE flag get_flush_inputs_to_zero(float_status *status)
+{
+ return STATUS(flush_inputs_to_zero);
+}
+INLINE flag get_default_nan_mode(float_status *status)
+{
+ return STATUS(default_nan_mode);
+}
/*----------------------------------------------------------------------------
| Routine to raise any or all of the software IEC/IEEE floating-point
@@ -225,25 +259,48 @@ enum {
/*----------------------------------------------------------------------------
| Software IEC/IEEE integer-to-floating-point conversion routines.
*----------------------------------------------------------------------------*/
-float32 int32_to_float32( int32 STATUS_PARAM );
-float64 int32_to_float64( int32 STATUS_PARAM );
-float32 uint32_to_float32( uint32 STATUS_PARAM );
-float64 uint32_to_float64( uint32 STATUS_PARAM );
-floatx80 int32_to_floatx80( int32 STATUS_PARAM );
-float128 int32_to_float128( int32 STATUS_PARAM );
-float32 int64_to_float32( int64 STATUS_PARAM );
-float32 uint64_to_float32( uint64 STATUS_PARAM );
-float64 int64_to_float64( int64 STATUS_PARAM );
-float64 uint64_to_float64( uint64 STATUS_PARAM );
-floatx80 int64_to_floatx80( int64 STATUS_PARAM );
-float128 int64_to_float128( int64 STATUS_PARAM );
-float128 uint64_to_float128( uint64 STATUS_PARAM );
+float32 int32_to_float32(int32_t STATUS_PARAM);
+float64 int32_to_float64(int32_t STATUS_PARAM);
+float32 uint32_to_float32(uint32_t STATUS_PARAM);
+float64 uint32_to_float64(uint32_t STATUS_PARAM);
+floatx80 int32_to_floatx80(int32_t STATUS_PARAM);
+float128 int32_to_float128(int32_t STATUS_PARAM);
+float32 int64_to_float32(int64_t STATUS_PARAM);
+float32 uint64_to_float32(uint64_t STATUS_PARAM);
+float64 int64_to_float64(int64_t STATUS_PARAM);
+float64 uint64_to_float64(uint64_t STATUS_PARAM);
+floatx80 int64_to_floatx80(int64_t STATUS_PARAM);
+float128 int64_to_float128(int64_t STATUS_PARAM);
+float128 uint64_to_float128(uint64_t STATUS_PARAM);
+
+/* We provide the int16 versions for symmetry of API with float-to-int */
+INLINE float32 int16_to_float32(int16_t v STATUS_PARAM)
+{
+ return int32_to_float32(v STATUS_VAR);
+}
+
+INLINE float32 uint16_to_float32(uint16_t v STATUS_PARAM)
+{
+ return uint32_to_float32(v STATUS_VAR);
+}
+
+INLINE float64 int16_to_float64(int16_t v STATUS_PARAM)
+{
+ return int32_to_float64(v STATUS_VAR);
+}
+
+INLINE float64 uint16_to_float64(uint16_t v STATUS_PARAM)
+{
+ return uint32_to_float64(v STATUS_VAR);
+}
/*----------------------------------------------------------------------------
| Software half-precision conversion routines.
*----------------------------------------------------------------------------*/
float16 float32_to_float16( float32, flag STATUS_PARAM );
float32 float16_to_float32( float16, flag STATUS_PARAM );
+float16 float64_to_float16(float64 a, flag ieee STATUS_PARAM);
+float64 float16_to_float64(float16 a, flag ieee STATUS_PARAM);
/*----------------------------------------------------------------------------
| Software half-precision operations.
@@ -265,6 +322,8 @@ extern const float16 float16_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE single-precision conversion routines.
*----------------------------------------------------------------------------*/
+int_fast16_t float32_to_int16(float32 STATUS_PARAM);
+uint_fast16_t float32_to_uint16(float32 STATUS_PARAM);
int_fast16_t float32_to_int16_round_to_zero(float32 STATUS_PARAM);
uint_fast16_t float32_to_uint16_round_to_zero(float32 STATUS_PARAM);
int32 float32_to_int32( float32 STATUS_PARAM );
@@ -272,6 +331,7 @@ int32 float32_to_int32_round_to_zero( float32 STATUS_PARAM );
uint32 float32_to_uint32( float32 STATUS_PARAM );
uint32 float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
int64 float32_to_int64( float32 STATUS_PARAM );
+uint64 float32_to_uint64(float32 STATUS_PARAM);
int64 float32_to_int64_round_to_zero( float32 STATUS_PARAM );
float64 float32_to_float64( float32 STATUS_PARAM );
floatx80 float32_to_floatx80( float32 STATUS_PARAM );
@@ -371,6 +431,8 @@ extern const float32 float32_default_nan;
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision conversion routines.
*----------------------------------------------------------------------------*/
+int_fast16_t float64_to_int16(float64 STATUS_PARAM);
+uint_fast16_t float64_to_uint16(float64 STATUS_PARAM);
int_fast16_t float64_to_int16_round_to_zero(float64 STATUS_PARAM);
uint_fast16_t float64_to_uint16_round_to_zero(float64 STATUS_PARAM);
int32 float64_to_int32( float64 STATUS_PARAM );
diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h
index 6bbcb1750d..3e53297a99 100644
--- a/include/hw/acpi/acpi.h
+++ b/include/hw/acpi/acpi.h
@@ -69,6 +69,12 @@
#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400
#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */
+#define ACPI_BITMASK_PM1_COMMON_ENABLED ( \
+ ACPI_BITMASK_RT_CLOCK_ENABLE | \
+ ACPI_BITMASK_POWER_BUTTON_ENABLE | \
+ ACPI_BITMASK_GLOBAL_LOCK_ENABLE | \
+ ACPI_BITMASK_TIMER_ENABLE)
+
/* PM1x_CNT */
#define ACPI_BITMASK_SCI_ENABLE 0x0001
#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002
@@ -160,6 +166,8 @@ void acpi_gpe_reset(ACPIREGS *ar);
void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val);
uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr);
+void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq);
+
/* acpi.c */
extern int acpi_enabled;
extern char unsigned *acpi_tables;
diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h
new file mode 100644
index 0000000000..da36647f32
--- /dev/null
+++ b/include/hw/arm/allwinner-a10.h
@@ -0,0 +1,35 @@
+#ifndef ALLWINNER_H_
+
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/char/serial.h"
+#include "hw/arm/arm.h"
+#include "hw/timer/allwinner-a10-pit.h"
+#include "hw/intc/allwinner-a10-pic.h"
+
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+
+
+#define AW_A10_PIC_REG_BASE 0x01c20400
+#define AW_A10_PIT_REG_BASE 0x01c20c00
+#define AW_A10_UART0_REG_BASE 0x01c28000
+
+#define AW_A10_SDRAM_BASE 0x40000000
+
+#define TYPE_AW_A10 "allwinner-a10"
+#define AW_A10(obj) OBJECT_CHECK(AwA10State, (obj), TYPE_AW_A10)
+
+typedef struct AwA10State {
+ /*< private >*/
+ DeviceState parent_obj;
+ /*< public >*/
+
+ ARMCPU cpu;
+ qemu_irq irq[AW_A10_PIC_INT_NR];
+ AwA10PITState timer;
+ AwA10PICState intc;
+} AwA10State;
+
+#define ALLWINNER_H_
+#endif
diff --git a/include/hw/arm/digic.h b/include/hw/arm/digic.h
new file mode 100644
index 0000000000..a739d6ae65
--- /dev/null
+++ b/include/hw/arm/digic.h
@@ -0,0 +1,43 @@
+/*
+ * Misc Canon DIGIC declarations.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef HW_ARM_DIGIC_H
+#define HW_ARM_DIGIC_H
+
+#include "cpu.h"
+
+#include "hw/timer/digic-timer.h"
+#include "hw/char/digic-uart.h"
+
+#define TYPE_DIGIC "digic"
+
+#define DIGIC(obj) OBJECT_CHECK(DigicState, (obj), TYPE_DIGIC)
+
+#define DIGIC4_NB_TIMERS 3
+
+typedef struct DigicState {
+ /*< private >*/
+ DeviceState parent_obj;
+ /*< public >*/
+
+ ARMCPU cpu;
+
+ DigicTimerState timer[DIGIC4_NB_TIMERS];
+ DigicUartState uart;
+} DigicState;
+
+#endif /* HW_ARM_DIGIC_H */
diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h
index a4e1a66264..7ca330a61f 100644
--- a/include/hw/arm/pxa.h
+++ b/include/hw/arm/pxa.h
@@ -102,15 +102,15 @@ void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq);
/* pxa2xx_keypad.c */
struct keymap {
- int column;
- int row;
+ int8_t column;
+ int8_t row;
};
typedef struct PXA2xxKeyPadState PXA2xxKeyPadState;
PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
hwaddr base,
qemu_irq irq);
-void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
- int size);
+void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
+ const struct keymap *map, int size);
/* pxa2xx.c */
typedef struct PXA2xxI2CState PXA2xxI2CState;
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 5a7ae9f59b..2151460f9e 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -6,7 +6,10 @@
#include "sysemu/blockdev.h"
#include "hw/qdev.h"
+typedef struct QEMUMachine QEMUMachine;
+
typedef struct QEMUMachineInitArgs {
+ const QEMUMachine *machine;
ram_addr_t ram_size;
const char *boot_order;
const char *kernel_filename;
@@ -21,7 +24,7 @@ typedef void QEMUMachineResetFunc(void);
typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp);
-typedef struct QEMUMachine {
+struct QEMUMachine {
const char *name;
const char *alias;
const char *desc;
@@ -43,7 +46,7 @@ typedef struct QEMUMachine {
GlobalProperty *compat_props;
struct QEMUMachine *next;
const char *hw_version;
-} QEMUMachine;
+};
int qemu_register_machine(QEMUMachine *m);
QEMUMachine *find_default_machine(void);
diff --git a/include/hw/char/digic-uart.h b/include/hw/char/digic-uart.h
new file mode 100644
index 0000000000..ef83a3059c
--- /dev/null
+++ b/include/hw/char/digic-uart.h
@@ -0,0 +1,47 @@
+/*
+ * Canon DIGIC UART block declarations.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef HW_CHAR_DIGIC_UART_H
+#define HW_CHAR_DIGIC_UART_H
+
+#include "hw/sysbus.h"
+#include "qemu/typedefs.h"
+
+#define TYPE_DIGIC_UART "digic-uart"
+#define DIGIC_UART(obj) \
+ OBJECT_CHECK(DigicUartState, (obj), TYPE_DIGIC_UART)
+
+enum {
+ R_TX = 0x00,
+ R_RX,
+ R_ST = (0x14 >> 2),
+ R_MAX
+};
+
+typedef struct DigicUartState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion regs_region;
+ CharDriverState *chr;
+
+ uint32_t reg_rx;
+ uint32_t reg_st;
+} DigicUartState;
+
+#endif /* HW_CHAR_DIGIC_UART_H */
diff --git a/include/hw/cpu/icc_bus.h b/include/hw/cpu/icc_bus.h
index b5500708dc..98a979fa1c 100644
--- a/include/hw/cpu/icc_bus.h
+++ b/include/hw/cpu/icc_bus.h
@@ -66,7 +66,7 @@ typedef struct ICCDeviceClass {
DeviceClass parent_class;
/*< public >*/
- int (*init)(ICCDevice *dev); /* TODO replace with QOM realize */
+ DeviceRealize realize;
} ICCDeviceClass;
#define TYPE_ICC_DEVICE "icc-device"
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index 1b0a7fbfad..70542a6f43 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -80,7 +80,7 @@ typedef struct APICCommonClass
{
ICCDeviceClass parent_class;
- void (*init)(APICCommonState *s);
+ DeviceRealize realize;
void (*set_base)(APICCommonState *s, uint64_t val);
void (*set_tpr)(APICCommonState *s, uint8_t val);
uint8_t (*get_tpr)(APICCommonState *s);
diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h
index 25576c819e..3be3352185 100644
--- a/include/hw/i386/ioapic_internal.h
+++ b/include/hw/i386/ioapic_internal.h
@@ -83,7 +83,8 @@ typedef struct IOAPICCommonState IOAPICCommonState;
typedef struct IOAPICCommonClass {
SysBusDeviceClass parent_class;
- void (*init)(IOAPICCommonState *s, int instance_no);
+
+ DeviceRealize realize;
void (*pre_save)(IOAPICCommonState *s);
void (*post_load)(IOAPICCommonState *s);
} IOAPICCommonClass;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 09652fb22c..7fe2bd17f6 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -13,6 +13,8 @@
#include "sysemu/sysemu.h"
#include "hw/pci/pci.h"
+#define HPET_INTCAP "hpet-intcap"
+
/* PC-style peripherals (also used by other machines). */
typedef struct PcPciInfo {
@@ -128,17 +130,9 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
#define PCI_HOST_PROP_PCI_HOLE64_SIZE "pci-hole64-size"
#define DEFAULT_PCI_HOLE64_SIZE (~0x0ULL)
-static inline uint64_t pci_host_get_hole64_size(uint64_t pci_hole64_size)
-{
- if (pci_hole64_size == DEFAULT_PCI_HOLE64_SIZE) {
- return 1ULL << 62;
- } else {
- return pci_hole64_size;
- }
-}
-void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start,
- uint64_t pci_hole64_size);
+void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
+ MemoryRegion *pci_address_space);
FWCfgState *pc_memory_init(MemoryRegion *system_memory,
const char *kernel_filename,
@@ -154,7 +148,8 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
ISADevice **floppy,
- bool no_vmport);
+ bool no_vmport,
+ uint32 hpet_irqs);
void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd);
void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
const char *boot_device,
@@ -187,8 +182,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
ram_addr_t ram_size,
- hwaddr pci_hole_start,
- hwaddr pci_hole_size,
+ ram_addr_t below_4g_mem_size,
ram_addr_t above_4g_mem_size,
MemoryRegion *pci_memory,
MemoryRegion *ram_memory);
@@ -246,7 +240,35 @@ uint16_t pvpanic_port(void);
int e820_add_entry(uint64_t, uint64_t, uint32_t);
+#define PC_Q35_COMPAT_1_7 \
+ PC_COMPAT_1_7, \
+ {\
+ .driver = "hpet",\
+ .property = HPET_INTCAP,\
+ .value = stringify(4),\
+ }
+
+#define PC_Q35_COMPAT_1_6 \
+ PC_COMPAT_1_6, \
+ PC_Q35_COMPAT_1_7
+
+#define PC_Q35_COMPAT_1_5 \
+ PC_COMPAT_1_5, \
+ PC_Q35_COMPAT_1_6
+
+#define PC_Q35_COMPAT_1_4 \
+ PC_COMPAT_1_4, \
+ PC_Q35_COMPAT_1_5
+
+#define PC_COMPAT_1_7 \
+ {\
+ .driver = TYPE_USB_DEVICE,\
+ .property = "msos-desc",\
+ .value = "no",\
+ }
+
#define PC_COMPAT_1_6 \
+ PC_COMPAT_1_7, \
{\
.driver = "e1000",\
.property = "mitigation",\
diff --git a/include/hw/i386/smbios.h b/include/hw/i386/smbios.h
index b08ec713f2..18fb970643 100644
--- a/include/hw/i386/smbios.h
+++ b/include/hw/i386/smbios.h
@@ -16,6 +16,8 @@
#include "qemu/option.h"
void smbios_entry_add(QemuOpts *opts);
+void smbios_set_type1_defaults(const char *manufacturer,
+ const char *product, const char *version);
uint8_t *smbios_get_table(size_t *length);
/*
diff --git a/include/hw/intc/allwinner-a10-pic.h b/include/hw/intc/allwinner-a10-pic.h
new file mode 100644
index 0000000000..5721b2e6b6
--- /dev/null
+++ b/include/hw/intc/allwinner-a10-pic.h
@@ -0,0 +1,40 @@
+#ifndef AW_A10_PIC_H
+#define AW_A10_PIC_H
+
+#define TYPE_AW_A10_PIC "allwinner-a10-pic"
+#define AW_A10_PIC(obj) OBJECT_CHECK(AwA10PICState, (obj), TYPE_AW_A10_PIC)
+
+#define AW_A10_PIC_VECTOR 0
+#define AW_A10_PIC_BASE_ADDR 4
+#define AW_A10_PIC_PROTECT 8
+#define AW_A10_PIC_NMI 0xc
+#define AW_A10_PIC_IRQ_PENDING 0x10
+#define AW_A10_PIC_FIQ_PENDING 0x20
+#define AW_A10_PIC_SELECT 0x30
+#define AW_A10_PIC_ENABLE 0x40
+#define AW_A10_PIC_MASK 0x50
+
+#define AW_A10_PIC_INT_NR 95
+#define AW_A10_PIC_REG_NUM DIV_ROUND_UP(AW_A10_PIC_INT_NR, 32)
+
+typedef struct AwA10PICState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+ MemoryRegion iomem;
+ qemu_irq parent_fiq;
+ qemu_irq parent_irq;
+
+ uint32_t vector;
+ uint32_t base_addr;
+ uint32_t protect;
+ uint32_t nmi;
+ uint32_t irq_pending[AW_A10_PIC_REG_NUM];
+ uint32_t fiq_pending[AW_A10_PIC_REG_NUM];
+ uint32_t select[AW_A10_PIC_REG_NUM];
+ uint32_t enable[AW_A10_PIC_REG_NUM];
+ uint32_t mask[AW_A10_PIC_REG_NUM];
+ /*priority setting here*/
+} AwA10PICState;
+
+#endif
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 4f381bdce7..0d232dfb67 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -37,7 +37,7 @@ typedef struct gic_irq_state {
uint8_t active;
uint8_t level;
bool model; /* 0 = N:N, 1 = 1:N */
- bool trigger; /* nonzero = edge triggered. */
+ bool edge_trigger; /* true: edge-triggered, false: level-triggered */
} gic_irq_state;
typedef struct GICState {
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index 309065fa41..d0355b712b 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -53,8 +53,6 @@ typedef struct MCHPCIState {
MemoryRegion *address_space_io;
PAMMemoryRegion pam_regions[13];
MemoryRegion smram_region;
- MemoryRegion pci_hole;
- MemoryRegion pci_hole_64bit;
PcPciInfo pci_info;
uint8_t smm_enabled;
ram_addr_t below_4g_mem_size;
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index b783e68d08..754b82de81 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -373,7 +373,6 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus);
void pci_device_set_intx_routing_notifier(PCIDevice *dev,
PCIINTxRoutingNotifier notifier);
void pci_device_reset(PCIDevice *dev);
-void pci_bus_reset(PCIBus *bus);
PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
const char *default_model,
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index fdaab2de52..b2f11e9a2c 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -332,6 +332,15 @@ static inline int spapr_allocate_lsi(int hint)
return spapr_allocate_irq(hint, true);
}
+/* RTAS return codes */
+#define RTAS_OUT_SUCCESS 0
+#define RTAS_OUT_NO_ERRORS_FOUND 1
+#define RTAS_OUT_HW_ERROR -1
+#define RTAS_OUT_BUSY -2
+#define RTAS_OUT_PARAM_ERROR -3
+#define RTAS_OUT_NOT_SUPPORTED -3
+#define RTAS_OUT_NOT_AUTHORIZED -9002
+
static inline uint64_t ppc64_phys_to_real(uint64_t addr)
{
return addr & ~0xF000000000000000ULL;
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 28fcaf17f8..a33edf4b0c 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -36,4 +36,8 @@ extern const VMStateDescription vmstate_ptimer;
.offset = vmstate_offset_pointer(_state, _field, ptimer_state), \
}
+#define VMSTATE_PTIMER_ARRAY(_f, _s, _n) \
+ VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, 0, \
+ vmstate_ptimer, ptimer_state)
+
#endif
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index f2043a69c2..2c4f140b9c 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -97,7 +97,18 @@ typedef struct DeviceClass {
const char *fw_name;
const char *desc;
Property *props;
- int no_user;
+
+ /*
+ * Shall we hide this device model from -device / device_add?
+ * All devices should support instantiation with device_add, and
+ * this flag should not exist. But we're not there, yet. Some
+ * devices fail to instantiate with cryptic error messages.
+ * Others instantiate, but don't work. Exposing users to such
+ * behavior would be cruel; this flag serves to protect them. It
+ * should never be set without a comment explaining why it is set.
+ * TODO remove once we're there
+ */
+ bool cannot_instantiate_with_device_add_yet;
/* callbacks */
void (*reset)(DeviceState *dev);
@@ -158,7 +169,7 @@ struct BusClass {
* bindings can be found at http://playground.sun.com/1275/bindings/.
*/
char *(*get_fw_dev_path)(DeviceState *dev);
- int (*reset)(BusState *bus);
+ void (*reset)(BusState *bus);
/* maximum devices allowed on the bus, 0: no limit. */
int max_dev;
};
@@ -253,10 +264,15 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
* < 0 if either devfn or busfn terminate walk somewhere in cursion,
* 0 otherwise. */
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque);
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
- qbus_walkerfn *busfn, void *opaque);
+int qbus_walk_children(BusState *bus,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque);
+int qdev_walk_children(DeviceState *dev,
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
+ void *opaque);
+
void qdev_reset_all(DeviceState *dev);
/**
@@ -272,8 +288,6 @@ void qdev_reset_all(DeviceState *dev);
void qbus_reset_all(BusState *bus);
void qbus_reset_all_fn(void *opaque);
-void qbus_free(BusState *bus);
-
/* This should go away once we get rid of the NULL bus hack */
BusState *sysbus_get_default(void);
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 692f82e935..77c6f7c037 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -122,8 +122,25 @@ extern PropertyInfo qdev_prop_arraylen;
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t)
+/*
+ * Please avoid pointer properties. If you must use them, you must
+ * cover them in their device's class init function as follows:
+ *
+ * - If the property must be set, the device cannot be used with
+ * device_add, so add code like this:
+ * |* Reason: pointer property "NAME-OF-YOUR-PROP" *|
+ * DeviceClass *dc = DEVICE_CLASS(class);
+ * dc->cannot_instantiate_with_device_add_yet = true;
+ *
+ * - If the property may safely remain null, document it like this:
+ * |*
+ * * Note: pointer property "interrupt_vector" may remain null, thus
+ * * no need for dc->cannot_instantiate_with_device_add_yet = true;
+ * *|
+ */
#define DEFINE_PROP_PTR(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*)
+
#define DEFINE_PROP_CHR(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharDriverState*)
#define DEFINE_PROP_STRING(_n, _s, _f) \
diff --git a/include/hw/timer/allwinner-a10-pit.h b/include/hw/timer/allwinner-a10-pit.h
new file mode 100644
index 0000000000..15efab8b5f
--- /dev/null
+++ b/include/hw/timer/allwinner-a10-pit.h
@@ -0,0 +1,58 @@
+#ifndef AW_A10_PIT_H
+#define AW_A10_PIT_H
+
+#include "hw/ptimer.h"
+
+#define TYPE_AW_A10_PIT "allwinner-A10-timer"
+#define AW_A10_PIT(obj) OBJECT_CHECK(AwA10PITState, (obj), TYPE_AW_A10_PIT)
+
+#define AW_A10_PIT_TIMER_NR 6
+#define AW_A10_PIT_TIMER_IRQ 0x1
+#define AW_A10_PIT_WDOG_IRQ 0x100
+
+#define AW_A10_PIT_TIMER_IRQ_EN 0
+#define AW_A10_PIT_TIMER_IRQ_ST 0x4
+
+#define AW_A10_PIT_TIMER_CONTROL 0x0
+#define AW_A10_PIT_TIMER_EN 0x1
+#define AW_A10_PIT_TIMER_RELOAD 0x2
+#define AW_A10_PIT_TIMER_MODE 0x80
+
+#define AW_A10_PIT_TIMER_INTERVAL 0x4
+#define AW_A10_PIT_TIMER_COUNT 0x8
+#define AW_A10_PIT_WDOG_CONTROL 0x90
+#define AW_A10_PIT_WDOG_MODE 0x94
+
+#define AW_A10_PIT_COUNT_CTL 0xa0
+#define AW_A10_PIT_COUNT_RL_EN 0x2
+#define AW_A10_PIT_COUNT_CLR_EN 0x1
+#define AW_A10_PIT_COUNT_LO 0xa4
+#define AW_A10_PIT_COUNT_HI 0xa8
+
+#define AW_A10_PIT_TIMER_BASE 0x10
+#define AW_A10_PIT_TIMER_BASE_END \
+ (AW_A10_PIT_TIMER_BASE * 6 + AW_A10_PIT_TIMER_COUNT)
+
+#define AW_A10_PIT_DEFAULT_CLOCK 0x4
+
+typedef struct AwA10PITState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+ qemu_irq irq[AW_A10_PIT_TIMER_NR];
+ ptimer_state * timer[AW_A10_PIT_TIMER_NR];
+ MemoryRegion iomem;
+
+ uint32_t irq_enable;
+ uint32_t irq_status;
+ uint32_t control[AW_A10_PIT_TIMER_NR];
+ uint32_t interval[AW_A10_PIT_TIMER_NR];
+ uint32_t count[AW_A10_PIT_TIMER_NR];
+ uint32_t watch_dog_mode;
+ uint32_t watch_dog_control;
+ uint32_t count_lo;
+ uint32_t count_hi;
+ uint32_t count_ctl;
+} AwA10PITState;
+
+#endif
diff --git a/include/hw/timer/digic-timer.h b/include/hw/timer/digic-timer.h
new file mode 100644
index 0000000000..ae913482c6
--- /dev/null
+++ b/include/hw/timer/digic-timer.h
@@ -0,0 +1,46 @@
+/*
+ * Canon DIGIC timer block declarations.
+ *
+ * Copyright (C) 2013 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef HW_TIMER_DIGIC_TIMER_H
+#define HW_TIMER_DIGIC_TIMER_H
+
+#include "hw/sysbus.h"
+#include "qemu/typedefs.h"
+#include "hw/ptimer.h"
+
+#define TYPE_DIGIC_TIMER "digic-timer"
+#define DIGIC_TIMER(obj) OBJECT_CHECK(DigicTimerState, (obj), TYPE_DIGIC_TIMER)
+
+#define DIGIC_TIMER_CONTROL 0x00
+#define DIGIC_TIMER_CONTROL_RST 0x80000000
+#define DIGIC_TIMER_CONTROL_EN 0x00000001
+#define DIGIC_TIMER_RELVALUE 0x08
+#define DIGIC_TIMER_VALUE 0x0c
+
+typedef struct DigicTimerState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ ptimer_state *ptimer;
+
+ uint32_t control;
+ uint32_t relvalue;
+} DigicTimerState;
+
+#endif /* HW_TIMER_DIGIC_TIMER_H */
diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h
index ab44bd31fd..773953be75 100644
--- a/include/hw/timer/hpet.h
+++ b/include/hw/timer/hpet.h
@@ -13,6 +13,8 @@
#ifndef QEMU_HPET_EMUL_H
#define QEMU_HPET_EMUL_H
+#include "qom/object.h"
+
#define HPET_BASE 0xfed00000
#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/
@@ -72,5 +74,11 @@ struct hpet_fw_config
extern struct hpet_fw_config hpet_cfg;
-bool hpet_find(void);
+#define TYPE_HPET "hpet"
+
+static inline bool hpet_find(void)
+{
+ return object_resolve_path_type("", TYPE_HPET, NULL);
+}
+
#endif
diff --git a/include/hw/usb.h b/include/hw/usb.h
index 2a3ea0c92e..3ef7af7413 100644
--- a/include/hw/usb.h
+++ b/include/hw/usb.h
@@ -182,6 +182,7 @@ typedef struct USBDescIface USBDescIface;
typedef struct USBDescEndpoint USBDescEndpoint;
typedef struct USBDescOther USBDescOther;
typedef struct USBDescString USBDescString;
+typedef struct USBDescMSOS USBDescMSOS;
struct USBDescString {
uint8_t index;
@@ -208,6 +209,8 @@ struct USBEndpoint {
enum USBDeviceFlags {
USB_DEV_FLAG_FULL_PATH,
USB_DEV_FLAG_IS_HOST,
+ USB_DEV_FLAG_MSOS_DESC_ENABLE,
+ USB_DEV_FLAG_MSOS_DESC_IN_USE,
};
/* definition of a USB device */
diff --git a/include/hw/virtio/dataplane/hostmem.h b/include/hw/virtio/dataplane/hostmem.h
deleted file mode 100644
index 2810f4b44e..0000000000
--- a/include/hw/virtio/dataplane/hostmem.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Thread-safe guest to host memory mapping
- *
- * Copyright 2012 Red Hat, Inc. and/or its affiliates
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@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.
- *
- */
-
-#ifndef HOSTMEM_H
-#define HOSTMEM_H
-
-#include "exec/memory.h"
-#include "qemu/thread.h"
-
-typedef struct {
- MemoryRegion *mr;
- void *host_addr;
- hwaddr guest_addr;
- uint64_t size;
- bool readonly;
-} HostMemRegion;
-
-typedef struct {
- /* The listener is invoked when regions change and a new list of regions is
- * built up completely before they are installed.
- */
- MemoryListener listener;
- HostMemRegion *new_regions;
- size_t num_new_regions;
-
- /* Current regions are accessed from multiple threads either to lookup
- * addresses or to install a new list of regions. The lock protects the
- * pointer and the regions.
- */
- QemuMutex current_regions_lock;
- HostMemRegion *current_regions;
- size_t num_current_regions;
-} HostMem;
-
-void hostmem_init(HostMem *hostmem);
-void hostmem_finalize(HostMem *hostmem);
-
-/**
- * Map a guest physical address to a pointer
- *
- * Note that there is map/unmap mechanism here. The caller must ensure that
- * mapped memory is no longer used across events like hot memory unplug. This
- * can be done with other mechanisms like bdrv_drain_all() that quiesce
- * in-flight I/O.
- */
-void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write);
-
-#endif /* HOSTMEM_H */
diff --git a/include/hw/virtio/dataplane/vring.h b/include/hw/virtio/dataplane/vring.h
index c0b69ff18f..63e7bf4256 100644
--- a/include/hw/virtio/dataplane/vring.h
+++ b/include/hw/virtio/dataplane/vring.h
@@ -19,11 +19,10 @@
#include <linux/virtio_ring.h>
#include "qemu-common.h"
-#include "hostmem.h"
#include "hw/virtio/virtio.h"
typedef struct {
- HostMem hostmem; /* guest memory mapper */
+ MemoryRegion *mr; /* memory region containing the vring */
struct vring vr; /* virtqueue vring mapped to host memory */
uint16_t last_avail_idx; /* last processed avail ring index */
uint16_t last_used_idx; /* last processed used ring index */
@@ -54,9 +53,8 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n);
void vring_disable_notification(VirtIODevice *vdev, Vring *vring);
bool vring_enable_notification(VirtIODevice *vdev, Vring *vring);
bool vring_should_notify(VirtIODevice *vdev, Vring *vring);
-int vring_pop(VirtIODevice *vdev, Vring *vring,
- struct iovec iov[], struct iovec *iov_end,
- unsigned int *out_num, unsigned int *in_num);
-void vring_push(Vring *vring, unsigned int head, int len);
+int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement **elem);
+void vring_push(Vring *vring, VirtQueueElement *elem, int len);
+void vring_free_element(VirtQueueElement *elem);
#endif /* VRING_H */
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
index 9217f85abc..0756545d4d 100644
--- a/include/hw/virtio/virtio-bus.h
+++ b/include/hw/virtio/virtio-bus.h
@@ -61,7 +61,7 @@ typedef struct VirtioBusClass {
* transport independent exit function.
* This is called by virtio-bus just before the device is unplugged.
*/
- void (*device_unplug)(DeviceState *d);
+ void (*device_unplugged)(DeviceState *d);
/*
* Does the transport have variable vring alignment?
* (ie can it ever call virtio_queue_set_align()?)
@@ -72,15 +72,11 @@ typedef struct VirtioBusClass {
struct VirtioBusState {
BusState parent_obj;
- /*
- * Only one VirtIODevice can be plugged on the bus.
- */
- VirtIODevice *vdev;
};
-int virtio_bus_plug_device(VirtIODevice *vdev);
+int virtio_bus_device_plugged(VirtIODevice *vdev);
void virtio_bus_reset(VirtioBusState *bus);
-void virtio_bus_destroy_device(VirtioBusState *bus);
+void virtio_bus_device_unplugged(VirtIODevice *bus);
/* Get the device id of the plugged device. */
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus);
/* Get the config_len field of the plugged device. */
@@ -98,4 +94,16 @@ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config);
/* Set config of the plugged device. */
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config);
+static inline VirtIODevice *virtio_bus_get_device(VirtioBusState *bus)
+{
+ BusState *qbus = &bus->parent_obj;
+ BusChild *kid = QTAILQ_FIRST(&qbus->children);
+ DeviceState *qdev = kid ? kid->child : NULL;
+
+ /* This is used on the data path, the cast is guaranteed
+ * to succeed by the qdev machinery.
+ */
+ return (VirtIODevice *)qdev;
+}
+
#endif /* VIRTIO_BUS_H */
diff --git a/include/hw/virtio/virtio-rng.h b/include/hw/virtio/virtio-rng.h
index debaa15d5a..14e85a5a3a 100644
--- a/include/hw/virtio/virtio-rng.h
+++ b/include/hw/virtio/virtio-rng.h
@@ -18,6 +18,8 @@
#define TYPE_VIRTIO_RNG "virtio-rng-device"
#define VIRTIO_RNG(obj) \
OBJECT_CHECK(VirtIORNG, (obj), TYPE_VIRTIO_RNG)
+#define VIRTIO_RNG_GET_PARENT_CLASS(obj) \
+ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_RNG)
/* The Virtio ID for the virtio rng device */
#define VIRTIO_ID_RNG 4
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
index 9a985403c2..42b102487a 100644
--- a/include/hw/virtio/virtio-scsi.h
+++ b/include/hw/virtio/virtio-scsi.h
@@ -186,7 +186,7 @@ typedef struct {
DEFINE_PROP_BIT("param_change", _state, _feature_field, \
VIRTIO_SCSI_F_CHANGE, true)
-int virtio_scsi_common_init(VirtIOSCSICommon *vs);
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs);
+void virtio_scsi_common_realize(DeviceState *dev, Error **errp);
+void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp);
#endif /* _QEMU_VIRTIO_SCSI_H */
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a90522d6d6..3e54e90aad 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -124,9 +124,13 @@ struct VirtIODevice
};
typedef struct VirtioDeviceClass {
- /* This is what a VirtioDevice must implement */
+ /*< private >*/
DeviceClass parent;
- int (*init)(VirtIODevice *vdev);
+ /*< public >*/
+
+ /* This is what a VirtioDevice must implement */
+ DeviceRealize realize;
+ DeviceUnrealize unrealize;
uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
uint32_t (*bad_features)(VirtIODevice *vdev);
void (*set_features)(VirtIODevice *vdev, uint32_t val);
diff --git a/include/hw/xilinx.h b/include/hw/xilinx.h
index 0c0251a2e9..9d6debe4d0 100644
--- a/include/hw/xilinx.h
+++ b/include/hw/xilinx.h
@@ -59,16 +59,13 @@ xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *ds,
StreamSlave *cs, hwaddr base, qemu_irq irq, int txmem,
int rxmem)
{
- Error *errp = NULL;
-
qdev_set_nic_properties(dev, nd);
qdev_prop_set_uint32(dev, "rxmem", rxmem);
qdev_prop_set_uint32(dev, "txmem", txmem);
object_property_set_link(OBJECT(dev), OBJECT(ds),
- "axistream-connected", &errp);
+ "axistream-connected", &error_abort);
object_property_set_link(OBJECT(dev), OBJECT(cs),
- "axistream-control-connected", &errp);
- assert_no_error(errp);
+ "axistream-control-connected", &error_abort);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
@@ -78,14 +75,11 @@ static inline void
xilinx_axidma_init(DeviceState *dev, StreamSlave *ds, StreamSlave *cs,
hwaddr base, qemu_irq irq, qemu_irq irq2, int freqhz)
{
- Error *errp = NULL;
-
qdev_prop_set_uint32(dev, "freqhz", freqhz);
object_property_set_link(OBJECT(dev), OBJECT(ds),
- "axistream-connected", &errp);
+ "axistream-connected", &error_abort);
object_property_set_link(OBJECT(dev), OBJECT(cs),
- "axistream-control-connected", &errp);
- assert_no_error(errp);
+ "axistream-control-connected", &error_abort);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
diff --git a/include/migration/migration.h b/include/migration/migration.h
index 140e6b471c..bfa3951a61 100644
--- a/include/migration/migration.h
+++ b/include/migration/migration.h
@@ -23,6 +23,17 @@
#include "qapi-types.h"
#include "exec/cpu-common.h"
+#define QEMU_VM_FILE_MAGIC 0x5145564d
+#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
+#define QEMU_VM_FILE_VERSION 0x00000003
+
+#define QEMU_VM_EOF 0x00
+#define QEMU_VM_SECTION_START 0x01
+#define QEMU_VM_SECTION_PART 0x02
+#define QEMU_VM_SECTION_END 0x03
+#define QEMU_VM_SECTION_FULL 0x04
+#define QEMU_VM_SUBSECTION 0x05
+
struct MigrationParams {
bool blk;
bool shared;
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 0f757fbeb6..a191fb6d8d 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -121,8 +121,11 @@ 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_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+int qemu_peek_byte(QEMUFile *f, int offset);
int qemu_get_byte(QEMUFile *f);
+void qemu_file_skip(QEMUFile *f, int size);
void qemu_update_position(QEMUFile *f, size_t size);
static inline unsigned int qemu_get_ubyte(QEMUFile *f)
@@ -141,6 +144,7 @@ void qemu_file_reset_rate_limit(QEMUFile *f);
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f);
int qemu_file_get_error(QEMUFile *f);
+void qemu_file_set_error(QEMUFile *f, int ret);
void qemu_fflush(QEMUFile *f);
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 9d09e60419..be193baba1 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -339,6 +339,16 @@ extern const VMStateInfo vmstate_info_bitmap;
.offset = vmstate_offset_array(_state, _field, _type, _num), \
}
+#define VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(_f, _s, _n, _v, _vmsd, _type) { \
+ .name = (stringify(_f)), \
+ .version_id = (_v), \
+ .num = (_n), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type *), \
+ .flags = VMS_ARRAY|VMS_STRUCT|VMS_ARRAY_OF_POINTER, \
+ .offset = vmstate_offset_array(_s, _f, _type*, _n), \
+}
+
#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \
.name = (stringify(_field)), \
.num = (_num), \
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 10fa0e390c..7e5f752b7a 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -5,7 +5,7 @@
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qdict.h"
#include "block/block.h"
-#include "monitor/readline.h"
+#include "qemu/readline.h"
extern Monitor *cur_mon;
extern Monitor *default_mon;
@@ -93,6 +93,9 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret);
int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret);
+int qmp_object_add(Monitor *mon, const QDict *qdict, QObject **ret);
+void object_add(const char *type, const char *id, const QDict *qdict,
+ Visitor *v, Error **errp);
AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
bool has_opaque, const char *opaque,
diff --git a/include/qapi/error.h b/include/qapi/error.h
index 7d4c6963d3..c0f0c3b432 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -95,4 +95,10 @@ void error_propagate(Error **dst_err, Error *local_err);
*/
void error_free(Error *err);
+/**
+ * If passed to error_set and friends, abort().
+ */
+
+extern Error *error_abort;
+
#endif
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 5cefd8022a..1ddf97b1c3 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -68,5 +68,6 @@ 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);
#endif /* QDICT_H */
diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
index c30c2f6d7a..73c67b70c2 100644
--- a/include/qapi/qmp/qerror.h
+++ b/include/qapi/qmp/qerror.h
@@ -29,7 +29,6 @@ typedef struct QError {
QString *qerror_human(const QError *qerror);
void qerror_report(ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void qerror_report_err(Error *err);
-void assert_no_error(Error *err);
/*
* QError class list
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 48a2a2edfd..29da211b47 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -13,6 +13,7 @@
#ifndef QAPI_VISITOR_CORE_H
#define QAPI_VISITOR_CORE_H
+#include "qemu/typedefs.h"
#include "qapi/qmp/qobject.h"
#include "qapi/error.h"
#include <stdlib.h>
@@ -26,8 +27,6 @@ typedef struct GenericList
struct GenericList *next;
} GenericList;
-typedef struct Visitor Visitor;
-
void visit_start_handle(Visitor *v, void **obj, const char *kind,
const char *name, Error **errp);
void visit_end_handle(Visitor *v, Error **errp);
diff --git a/include/qemu-io.h b/include/qemu-io.h
index a418b46a40..7e7c07c09b 100644
--- a/include/qemu-io.h
+++ b/include/qemu-io.h
@@ -42,5 +42,8 @@ bool qemuio_command(BlockDriverState *bs, const char *cmd);
void qemuio_add_command(const cmdinfo_t *ci);
int qemuio_command_usage(const cmdinfo_t *ci);
+void qemuio_complete_command(const char *input,
+ void (*fn)(const char *cmd, void *opaque),
+ void *opaque);
#endif /* QEMU_IO_H */
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index 308bbb71e9..1babd5d812 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -31,7 +31,7 @@
* bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2)
* bitmap_complement(dst, src, nbits) *dst = ~(*src)
* bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal?
- * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap?
+ * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap?
* 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
@@ -62,71 +62,71 @@
)
#define DECLARE_BITMAP(name,bits) \
- unsigned long name[BITS_TO_LONGS(bits)]
+ unsigned long name[BITS_TO_LONGS(bits)]
#define small_nbits(nbits) \
- ((nbits) <= BITS_PER_LONG)
+ ((nbits) <= BITS_PER_LONG)
-int slow_bitmap_empty(const unsigned long *bitmap, int bits);
-int slow_bitmap_full(const unsigned long *bitmap, int bits);
+int slow_bitmap_empty(const unsigned long *bitmap, long bits);
+int slow_bitmap_full(const unsigned long *bitmap, long bits);
int slow_bitmap_equal(const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
- int bits);
+ long bits);
void slow_bitmap_shift_right(unsigned long *dst,
- const unsigned long *src, int shift, int bits);
+ const unsigned long *src, int shift, long bits);
void slow_bitmap_shift_left(unsigned long *dst,
- const unsigned long *src, int shift, int bits);
+ const unsigned long *src, int shift, long bits);
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
int slow_bitmap_intersects(const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits);
+ const unsigned long *bitmap2, long bits);
-static inline unsigned long *bitmap_new(int nbits)
+static inline unsigned long *bitmap_new(long nbits)
{
- int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
return g_malloc0(len);
}
-static inline void bitmap_zero(unsigned long *dst, int nbits)
+static inline void bitmap_zero(unsigned long *dst, long nbits)
{
if (small_nbits(nbits)) {
*dst = 0UL;
} else {
- int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memset(dst, 0, len);
}
}
-static inline void bitmap_fill(unsigned long *dst, int nbits)
+static inline void bitmap_fill(unsigned long *dst, long nbits)
{
size_t nlongs = BITS_TO_LONGS(nbits);
if (!small_nbits(nbits)) {
- int len = (nlongs - 1) * sizeof(unsigned long);
+ long len = (nlongs - 1) * sizeof(unsigned long);
memset(dst, 0xff, len);
}
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
}
static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
- int nbits)
+ long nbits)
{
if (small_nbits(nbits)) {
*dst = *src;
} else {
- int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memcpy(dst, src, len);
}
}
static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
return (*dst = *src1 & *src2) != 0;
@@ -135,7 +135,7 @@ static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
}
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
*dst = *src1 | *src2;
@@ -145,7 +145,7 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
}
static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
*dst = *src1 ^ *src2;
@@ -155,7 +155,7 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
}
static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
return (*dst = *src1 & ~(*src2)) != 0;
@@ -163,8 +163,9 @@ static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
return slow_bitmap_andnot(dst, src1, src2, nbits);
}
-static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
- int nbits)
+static inline void bitmap_complement(unsigned long *dst,
+ const unsigned long *src,
+ long nbits)
{
if (small_nbits(nbits)) {
*dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits);
@@ -174,7 +175,7 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr
}
static inline int bitmap_equal(const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
@@ -183,7 +184,7 @@ static inline int bitmap_equal(const unsigned long *src1,
}
}
-static inline int bitmap_empty(const unsigned long *src, int nbits)
+static inline int bitmap_empty(const unsigned long *src, long nbits)
{
if (small_nbits(nbits)) {
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
@@ -192,7 +193,7 @@ static inline int bitmap_empty(const unsigned long *src, int nbits)
}
}
-static inline int bitmap_full(const unsigned long *src, int nbits)
+static inline int bitmap_full(const unsigned long *src, long nbits)
{
if (small_nbits(nbits)) {
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
@@ -202,7 +203,7 @@ static inline int bitmap_full(const unsigned long *src, int nbits)
}
static inline int bitmap_intersects(const unsigned long *src1,
- const unsigned long *src2, int nbits)
+ const unsigned long *src2, long nbits)
{
if (small_nbits(nbits)) {
return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
@@ -211,12 +212,21 @@ static inline int bitmap_intersects(const unsigned long *src1,
}
}
-void bitmap_set(unsigned long *map, int i, int len);
-void bitmap_clear(unsigned long *map, int start, int nr);
+void bitmap_set(unsigned long *map, long i, long len);
+void bitmap_clear(unsigned long *map, long start, long nr);
unsigned long bitmap_find_next_zero_area(unsigned long *map,
- unsigned long size,
- unsigned long start,
- unsigned int nr,
- unsigned long align_mask);
+ unsigned long size,
+ unsigned long start,
+ unsigned long nr,
+ unsigned long align_mask);
+
+static inline unsigned long *bitmap_zero_extend(unsigned long *old,
+ long old_nbits, long new_nbits)
+{
+ long new_len = BITS_TO_LONGS(new_nbits) * sizeof(unsigned long);
+ unsigned long *new = g_realloc(old, new_len);
+ bitmap_clear(new, old_nbits, new_nbits - old_nbits);
+ return new;
+}
#endif /* BITMAP_H */
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 304c90c2b4..340b1e73bd 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -28,7 +28,7 @@
* @nr: the bit to set
* @addr: the address to start counting from
*/
-static inline void set_bit(int nr, unsigned long *addr)
+static inline void set_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -41,7 +41,7 @@ static inline void set_bit(int nr, unsigned long *addr)
* @nr: Bit to clear
* @addr: Address to start counting from
*/
-static inline void clear_bit(int nr, unsigned long *addr)
+static inline void clear_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -54,7 +54,7 @@ static inline void clear_bit(int nr, unsigned long *addr)
* @nr: Bit to change
* @addr: Address to start counting from
*/
-static inline void change_bit(int nr, unsigned long *addr)
+static inline void change_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -67,7 +67,7 @@ static inline void change_bit(int nr, unsigned long *addr)
* @nr: Bit to set
* @addr: Address to count from
*/
-static inline int test_and_set_bit(int nr, unsigned long *addr)
+static inline int test_and_set_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -82,7 +82,7 @@ static inline int test_and_set_bit(int nr, unsigned long *addr)
* @nr: Bit to clear
* @addr: Address to count from
*/
-static inline int test_and_clear_bit(int nr, unsigned long *addr)
+static inline int test_and_clear_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -97,7 +97,7 @@ static inline int test_and_clear_bit(int nr, unsigned long *addr)
* @nr: Bit to change
* @addr: Address to count from
*/
-static inline int test_and_change_bit(int nr, unsigned long *addr)
+static inline int test_and_change_bit(long nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = addr + BIT_WORD(nr);
@@ -112,7 +112,7 @@ static inline int test_and_change_bit(int nr, unsigned long *addr)
* @nr: bit number to test
* @addr: Address to start counting from
*/
-static inline int test_bit(int nr, const unsigned long *addr)
+static inline int test_bit(long nr, const unsigned long *addr)
{
return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
}
diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
index 508428ff32..dbd97c4bdb 100644
--- a/include/qemu/config-file.h
+++ b/include/qemu/config-file.h
@@ -4,6 +4,7 @@
#include <stdio.h>
#include "qemu/option.h"
#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
QemuOptsList *qemu_find_opts(const char *group);
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
@@ -18,6 +19,11 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
int qemu_read_config_file(const char *filename);
+/* Parse QDict options as a replacement for a config file (allowing multiple
+ enumerated (0..(n-1)) configuration "sections") */
+void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
+ Error **errp);
+
/* Read default QEMU config files
*/
int qemu_read_default_config_files(bool userconfig);
diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h
index 0f688c1c00..de85d282d0 100644
--- a/include/qemu/host-utils.h
+++ b/include/qemu/host-utils.h
@@ -228,6 +228,38 @@ static inline int cto64(uint64_t val)
}
/**
+ * clrsb32 - count leading redundant sign bits in a 32-bit value.
+ * @val: The value to search
+ *
+ * Returns the number of bits following the sign bit that are equal to it.
+ * No special cases; output range is [0-31].
+ */
+static inline int clrsb32(uint32_t val)
+{
+#if QEMU_GNUC_PREREQ(4, 7)
+ return __builtin_clrsb(val);
+#else
+ return clz32(val ^ ((int32_t)val >> 1)) - 1;
+#endif
+}
+
+/**
+ * clrsb64 - count leading redundant sign bits in a 64-bit value.
+ * @val: The value to search
+ *
+ * Returns the number of bits following the sign bit that are equal to it.
+ * No special cases; output range is [0-63].
+ */
+static inline int clrsb64(uint64_t val)
+{
+#if QEMU_GNUC_PREREQ(4, 7)
+ return __builtin_clrsbll(val);
+#else
+ return clz64(val ^ ((int64_t)val >> 1)) - 1;
+#endif
+}
+
+/**
* ctpop8 - count the population of one bits in an 8-bit value.
* @val: The value to search
*/
diff --git a/include/qemu/option.h b/include/qemu/option.h
index 5c0c6dd294..3ea871a3ba 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -136,7 +136,6 @@ int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
int fail_if_exists, Error **errp);
-QemuOpts *qemu_opts_create_nofail(QemuOptsList *list);
void qemu_opts_reset(QemuOptsList *list);
void qemu_opts_loc_restore(QemuOpts *opts);
int qemu_opts_set(QemuOptsList *list, const char *id,
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index b3e2b6d8ea..eac7172bcb 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -240,4 +240,6 @@ static inline void qemu_init_auxval(char **envp) { }
void qemu_init_auxval(char **envp);
#endif
+void qemu_set_tty_echo(int fd, bool echo);
+
#endif
diff --git a/include/monitor/readline.h b/include/qemu/readline.h
index 0faf6e1db7..a89fe4a9a9 100644
--- a/include/monitor/readline.h
+++ b/include/qemu/readline.h
@@ -1,14 +1,15 @@
#ifndef READLINE_H
#define READLINE_H
-#include "qemu-common.h"
-
#define READLINE_CMD_BUF_SIZE 4095
#define READLINE_MAX_CMDS 64
#define READLINE_MAX_COMPLETIONS 256
-typedef void ReadLineFunc(Monitor *mon, const char *str, void *opaque);
-typedef void ReadLineCompletionFunc(Monitor *mon,
+typedef void ReadLinePrintfFunc(void *opaque, const char *fmt, ...);
+typedef void ReadLineFlushFunc(void *opaque);
+typedef void ReadLineFunc(void *opaque, const char *str,
+ void *readline_opaque);
+typedef void ReadLineCompletionFunc(void *opaque,
const char *cmdline);
typedef struct ReadLineState {
@@ -35,7 +36,10 @@ typedef struct ReadLineState {
void *readline_opaque;
int read_password;
char prompt[256];
- Monitor *mon;
+
+ ReadLinePrintfFunc *printf_func;
+ ReadLineFlushFunc *flush_func;
+ void *opaque;
} ReadLineState;
void readline_add_completion(ReadLineState *rs, const char *str);
@@ -46,11 +50,13 @@ const char *readline_get_history(ReadLineState *rs, unsigned int index);
void readline_handle_byte(ReadLineState *rs, int ch);
void readline_start(ReadLineState *rs, const char *prompt, int read_password,
- ReadLineFunc *readline_func, void *opaque);
+ ReadLineFunc *readline_func, void *readline_opaque);
void readline_restart(ReadLineState *rs);
void readline_show_prompt(ReadLineState *rs);
-ReadLineState *readline_init(Monitor *mon,
+ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
+ ReadLineFlushFunc *flush_func,
+ void *opaque,
ReadLineCompletionFunc *completion_finder);
#endif /* !READLINE_H */
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index a4c1b84d69..45244960b5 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -10,6 +10,8 @@ typedef struct QEMUBH QEMUBH;
typedef struct AioContext AioContext;
+typedef struct Visitor Visitor;
+
struct Monitor;
typedef struct Monitor Monitor;
typedef struct MigrationParams MigrationParams;
diff --git a/include/qom/object.h b/include/qom/object.h
index a275db2092..e0ff212cb6 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -358,7 +358,8 @@ struct ObjectClass
Type type;
GSList *interfaces;
- const char *cast_cache[OBJECT_CLASS_CAST_CACHE];
+ const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];
+ const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
ObjectUnparent *unparent;
};
@@ -535,6 +536,7 @@ struct InterfaceClass
ObjectClass parent_class;
/*< private >*/
ObjectClass *concrete_class;
+ Type interface_type;
};
#define TYPE_INTERFACE "interface"
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 10820910d7..134712b500 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -64,7 +64,7 @@ DriveInfo *drive_init(QemuOpts *arg, BlockInterfaceType block_default_type);
DriveInfo *add_init_drive(const char *opts);
void qmp_change_blockdev(const char *device, const char *filename,
- bool has_format, const char *format, Error **errp);
+ const char *format, Error **errp);
void do_commit(Monitor *mon, const QDict *qdict);
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
#endif
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index ad101d9258..b81a6ff185 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -69,6 +69,7 @@ struct CharDriverState {
void (*chr_accept_input)(struct CharDriverState *chr);
void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open);
+ void (*chr_fe_event)(struct CharDriverState *chr, int event);
void *opaque;
char *label;
char *filename;
@@ -138,6 +139,15 @@ void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo);
void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open);
/**
+ * @qemu_chr_fe_event:
+ *
+ * Send an event from the front end to the back end.
+ *
+ * @event the event to send
+ */
+void qemu_chr_fe_event(CharDriverState *s, int event);
+
+/**
* @qemu_chr_fe_printf:
*
* Write to a character backend using a printf style interface.
diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
index 2b58baf8b1..899f05c138 100644
--- a/include/sysemu/device_tree.h
+++ b/include/sysemu/device_tree.h
@@ -17,27 +17,27 @@
void *create_device_tree(int *sizep);
void *load_device_tree(const char *filename_path, int *sizep);
-int qemu_devtree_setprop(void *fdt, const char *node_path,
- const char *property, const void *val_array, int size);
-int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
- const char *property, uint32_t val);
-int qemu_devtree_setprop_u64(void *fdt, const char *node_path,
- const char *property, uint64_t val);
-int qemu_devtree_setprop_string(void *fdt, const char *node_path,
- const char *property, const char *string);
-int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
- const char *property,
- const char *target_node_path);
-const void *qemu_devtree_getprop(void *fdt, const char *node_path,
- const char *property, int *lenp);
-uint32_t qemu_devtree_getprop_cell(void *fdt, const char *node_path,
- const char *property);
-uint32_t qemu_devtree_get_phandle(void *fdt, const char *path);
-uint32_t qemu_devtree_alloc_phandle(void *fdt);
-int qemu_devtree_nop_node(void *fdt, const char *node_path);
-int qemu_devtree_add_subnode(void *fdt, const char *name);
+int qemu_fdt_setprop(void *fdt, const char *node_path,
+ const char *property, const void *val, int size);
+int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val);
+int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
+ const char *property, uint64_t val);
+int qemu_fdt_setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string);
+int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
+ const char *property,
+ const char *target_node_path);
+const void *qemu_fdt_getprop(void *fdt, const char *node_path,
+ const char *property, int *lenp);
+uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
+ const char *property);
+uint32_t qemu_fdt_get_phandle(void *fdt, const char *path);
+uint32_t qemu_fdt_alloc_phandle(void *fdt);
+int qemu_fdt_nop_node(void *fdt, const char *node_path);
+int qemu_fdt_add_subnode(void *fdt, const char *name);
-#define qemu_devtree_setprop_cells(fdt, node_path, property, ...) \
+#define qemu_fdt_setprop_cells(fdt, node_path, property, ...) \
do { \
uint32_t qdt_tmp[] = { __VA_ARGS__ }; \
int i; \
@@ -45,14 +45,14 @@ int qemu_devtree_add_subnode(void *fdt, const char *name);
for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) { \
qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]); \
} \
- qemu_devtree_setprop(fdt, node_path, property, qdt_tmp, \
- sizeof(qdt_tmp)); \
+ qemu_fdt_setprop(fdt, node_path, property, qdt_tmp, \
+ sizeof(qdt_tmp)); \
} while (0)
-void qemu_devtree_dumpdtb(void *fdt, int size);
+void qemu_fdt_dumpdtb(void *fdt, int size);
/**
- * qemu_devtree_setprop_sized_cells_from_array:
+ * qemu_fdt_setprop_sized_cells_from_array:
* @fdt: device tree blob
* @node_path: node to set property on
* @property: property to set
@@ -72,20 +72,20 @@ void qemu_devtree_dumpdtb(void *fdt, int size);
* the number of cells used for each element vary depending on the
* #address-cells and #size-cells properties of their parent node.
* If you know all your cell elements are one cell wide you can use the
- * simpler qemu_devtree_setprop_cells(). If you're not setting up the
- * array programmatically, qemu_devtree_setprop_sized_cells may be more
+ * simpler qemu_fdt_setprop_cells(). If you're not setting up the
+ * array programmatically, qemu_fdt_setprop_sized_cells may be more
* convenient.
*
* Return value: 0 on success, <0 on error.
*/
-int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
- const char *node_path,
- const char *property,
- int numvalues,
- uint64_t *values);
+int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
+ const char *node_path,
+ const char *property,
+ int numvalues,
+ uint64_t *values);
/**
- * qemu_devtree_setprop_sized_cells:
+ * qemu_fdt_setprop_sized_cells:
* @fdt: device tree blob
* @node_path: node to set property on
* @property: property to set
@@ -97,17 +97,17 @@ int qemu_devtree_setprop_sized_cells_from_array(void *fdt,
* used by this value" and "value".
*
* This is a convenience wrapper for the function
- * qemu_devtree_setprop_sized_cells_from_array().
+ * qemu_fdt_setprop_sized_cells_from_array().
*
* Return value: 0 on success, <0 on error.
*/
-#define qemu_devtree_setprop_sized_cells(fdt, node_path, property, ...) \
- ({ \
- uint64_t qdt_tmp[] = { __VA_ARGS__ }; \
- qemu_devtree_setprop_sized_cells_from_array(fdt, node_path, \
- property, \
- ARRAY_SIZE(qdt_tmp) / 2, \
- qdt_tmp); \
+#define qemu_fdt_setprop_sized_cells(fdt, node_path, property, ...) \
+ ({ \
+ uint64_t qdt_tmp[] = { __VA_ARGS__ }; \
+ qemu_fdt_setprop_sized_cells_from_array(fdt, node_path, \
+ property, \
+ ARRAY_SIZE(qdt_tmp) / 2, \
+ qdt_tmp); \
})
#endif /* __DEVICE_TREE_H__ */
diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index 9a0c6b31c8..112a661ac4 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -16,38 +16,23 @@
#include "qemu-common.h"
-#if !defined(CONFIG_USER_ONLY)
extern bool qtest_allowed;
-extern const char *qtest_chrdev;
-extern const char *qtest_log;
static inline bool qtest_enabled(void)
{
return qtest_allowed;
}
+int qtest_init_accel(void);
+void qtest_init(const char *qtest_chrdev, const char *qtest_log);
+
static inline int qtest_available(void)
{
+#ifdef CONFIG_POSIX
return 1;
-}
-
-int qtest_init(void);
#else
-static inline bool qtest_enabled(void)
-{
- return false;
-}
-
-static inline int qtest_available(void)
-{
- return 0;
-}
-
-static inline int qtest_init(void)
-{
return 0;
-}
-
#endif
+}
#endif
diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h
index 86c75c7a71..a93b4b2572 100644
--- a/include/ui/qemu-spice.h
+++ b/include/ui/qemu-spice.h
@@ -18,6 +18,8 @@
#ifndef QEMU_SPICE_H
#define QEMU_SPICE_H
+#include "config-host.h"
+
#ifdef CONFIG_SPICE
#include <spice.h>
diff --git a/kvm-all.c b/kvm-all.c
index eb38ee4eb9..a3fb8de268 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -31,6 +31,7 @@
#include "sysemu/kvm.h"
#include "qemu/bswap.h"
#include "exec/memory.h"
+#include "exec/ram_addr.h"
#include "exec/address-spaces.h"
#include "qemu/event_notifier.h"
#include "trace.h"
@@ -379,31 +380,10 @@ static int kvm_set_migration_log(int enable)
static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
unsigned long *bitmap)
{
- unsigned int i, j;
- unsigned long page_number, c;
- hwaddr addr, addr1;
- unsigned int pages = int128_get64(section->size) / getpagesize();
- unsigned int len = (pages + HOST_LONG_BITS - 1) / HOST_LONG_BITS;
- unsigned long hpratio = getpagesize() / TARGET_PAGE_SIZE;
+ ram_addr_t start = section->offset_within_region + section->mr->ram_addr;
+ ram_addr_t pages = int128_get64(section->size) / getpagesize();
- /*
- * bitmap-traveling is faster than memory-traveling (for addr...)
- * especially when most of the memory is not dirty.
- */
- for (i = 0; i < len; i++) {
- if (bitmap[i] != 0) {
- c = leul_to_cpu(bitmap[i]);
- do {
- j = ffsl(c) - 1;
- c &= ~(1ul << j);
- page_number = (i * HOST_LONG_BITS + j) * hpratio;
- addr1 = page_number * TARGET_PAGE_SIZE;
- addr = section->offset_within_region + addr1;
- memory_region_set_dirty(section->mr, addr,
- TARGET_PAGE_SIZE * hpratio);
- } while (c != 0);
- }
- }
+ cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
return 0;
}
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
index f1d46d397a..24f7088ecf 100644
--- a/libcacard/vscclient.c
+++ b/libcacard/vscclient.c
@@ -58,7 +58,7 @@ static QemuMutex socket_to_send_lock;
static guint socket_tag;
static void
-update_socket_watch(gboolean out);
+update_socket_watch(void);
static gboolean
do_socket_send(GIOChannel *source,
@@ -80,7 +80,7 @@ do_socket_send(GIOChannel *source,
g_byte_array_remove_range(socket_to_send, 0, bw);
if (socket_to_send->len == 0) {
- update_socket_watch(FALSE);
+ update_socket_watch();
return FALSE;
}
return TRUE;
@@ -89,7 +89,7 @@ do_socket_send(GIOChannel *source,
static gboolean
socket_prepare_sending(gpointer user_data)
{
- update_socket_watch(TRUE);
+ update_socket_watch();
return FALSE;
}
@@ -440,8 +440,10 @@ do_socket(GIOChannel *source,
}
static void
-update_socket_watch(gboolean out)
+update_socket_watch(void)
{
+ gboolean out = socket_to_send->len > 0;
+
if (socket_tag != 0) {
g_source_remove(socket_tag);
}
diff --git a/linux-user/aarch64/syscall.h b/linux-user/aarch64/syscall.h
index aef419efeb..18f44a8a40 100644
--- a/linux-user/aarch64/syscall.h
+++ b/linux-user/aarch64/syscall.h
@@ -7,3 +7,4 @@ struct target_pt_regs {
#define UNAME_MACHINE "aarch64"
#define UNAME_MINIMUM_RELEASE "3.8.0"
+#define TARGET_CLONE_BACKWARDS
diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h
index 6f5539b50f..21560ef832 100644
--- a/linux-user/aarch64/target_cpu.h
+++ b/linux-user/aarch64/target_cpu.h
@@ -29,7 +29,10 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)
static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls)
{
- env->sr.tpidr_el0 = newtls;
+ /* Note that AArch64 Linux keeps the TLS pointer in TPIDR; this is
+ * different from AArch32 Linux, which uses TPIDRRO.
+ */
+ env->cp15.tpidr_el0 = newtls;
}
#endif
diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h
index ed323c079d..39d65b692b 100644
--- a/linux-user/arm/target_cpu.h
+++ b/linux-user/arm/target_cpu.h
@@ -29,7 +29,7 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp)
static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls)
{
- env->cp15.c13_tls2 = newtls;
+ env->cp15.tpidrro_el0 = newtls;
}
#endif
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 8dd424dadd..5902f162b4 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1998,8 +1998,7 @@ give_up:
free(syms);
}
-int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
- struct image_info * info)
+int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
{
struct image_info interp_info;
struct elfhdr elf_ex;
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
index ceb89bb6ea..566a7a87a3 100644
--- a/linux-user/flatload.c
+++ b/linux-user/flatload.c
@@ -704,8 +704,7 @@ static int load_flat_shared_library(int id, struct lib_info *libs)
#endif /* CONFIG_BINFMT_SHARED_FLAT */
-int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
- struct image_info * info)
+int load_flt_binary(struct linux_binprm *bprm, struct image_info *info)
{
struct lib_info libinfo[MAX_SHARED_LIBS];
abi_ulong p = bprm->p;
diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c
index a1fe5ed9ae..f2997c2f4b 100644
--- a/linux-user/linuxload.c
+++ b/linux-user/linuxload.c
@@ -154,13 +154,13 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
&& bprm->buf[1] == 'E'
&& bprm->buf[2] == 'L'
&& bprm->buf[3] == 'F') {
- retval = load_elf_binary(bprm, regs, infop);
+ retval = load_elf_binary(bprm, infop);
#if defined(TARGET_HAS_BFLT)
} else if (bprm->buf[0] == 'b'
&& bprm->buf[1] == 'F'
&& bprm->buf[2] == 'L'
&& bprm->buf[3] == 'T') {
- retval = load_flt_binary(bprm,regs,infop);
+ retval = load_flt_binary(bprm, infop);
#endif
} else {
return -ENOEXEC;
diff --git a/linux-user/main.c b/linux-user/main.c
index 54f71fe8f6..cabc9e1a0e 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -566,7 +566,7 @@ do_kernel_trap(CPUARMState *env)
end_exclusive();
break;
case 0xffff0fe0: /* __kernel_get_tls */
- env->regs[0] = env->cp15.c13_tls2;
+ env->regs[0] = env->cp15.tpidrro_el0;
break;
case 0xffff0f60: /* __kernel_cmpxchg64 */
arm_kernel_cmpxchg64_helper(env);
@@ -585,20 +585,25 @@ do_kernel_trap(CPUARMState *env)
return 0;
}
-#endif
+/* Store exclusive handling for AArch32 */
static int do_strex(CPUARMState *env)
{
- uint32_t val;
+ uint64_t val;
int size;
int rc = 1;
int segv = 0;
uint32_t addr;
start_exclusive();
- addr = env->exclusive_addr;
- if (addr != env->exclusive_test) {
+ if (env->exclusive_addr != env->exclusive_test) {
goto fail;
}
+ /* We know we're always AArch32 so the address is in uint32_t range
+ * unless it was the -1 exclusive-monitor-lost value (which won't
+ * match exclusive_test above).
+ */
+ assert(extract64(env->exclusive_addr, 32, 32) == 0);
+ addr = env->exclusive_addr;
size = env->exclusive_info & 0xf;
switch (size) {
case 0:
@@ -618,19 +623,19 @@ static int do_strex(CPUARMState *env)
env->cp15.c6_data = addr;
goto done;
}
- if (val != env->exclusive_val) {
- goto fail;
- }
if (size == 3) {
- segv = get_user_u32(val, addr + 4);
+ uint32_t valhi;
+ segv = get_user_u32(valhi, addr + 4);
if (segv) {
env->cp15.c6_data = addr + 4;
goto done;
}
- if (val != env->exclusive_high) {
- goto fail;
- }
+ val = deposit64(val, 32, 32, valhi);
}
+ if (val != env->exclusive_val) {
+ goto fail;
+ }
+
val = env->regs[(env->exclusive_info >> 8) & 0xf];
switch (size) {
case 0:
@@ -665,7 +670,6 @@ done:
return segv;
}
-#ifdef TARGET_ABI32
void cpu_loop(CPUARMState *env)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
@@ -880,6 +884,122 @@ void cpu_loop(CPUARMState *env)
#else
+/*
+ * Handle AArch64 store-release exclusive
+ *
+ * rs = gets the status result of store exclusive
+ * rt = is the register that is stored
+ * rt2 = is the second register store (in STP)
+ *
+ */
+static int do_strex_a64(CPUARMState *env)
+{
+ uint64_t val;
+ int size;
+ bool is_pair;
+ int rc = 1;
+ int segv = 0;
+ uint64_t addr;
+ int rs, rt, rt2;
+
+ start_exclusive();
+ /* size | is_pair << 2 | (rs << 4) | (rt << 9) | (rt2 << 14)); */
+ size = extract32(env->exclusive_info, 0, 2);
+ is_pair = extract32(env->exclusive_info, 2, 1);
+ rs = extract32(env->exclusive_info, 4, 5);
+ rt = extract32(env->exclusive_info, 9, 5);
+ rt2 = extract32(env->exclusive_info, 14, 5);
+
+ addr = env->exclusive_addr;
+
+ if (addr != env->exclusive_test) {
+ goto finish;
+ }
+
+ switch (size) {
+ case 0:
+ segv = get_user_u8(val, addr);
+ break;
+ case 1:
+ segv = get_user_u16(val, addr);
+ break;
+ case 2:
+ segv = get_user_u32(val, addr);
+ break;
+ case 3:
+ segv = get_user_u64(val, addr);
+ break;
+ default:
+ abort();
+ }
+ if (segv) {
+ env->cp15.c6_data = addr;
+ goto error;
+ }
+ if (val != env->exclusive_val) {
+ goto finish;
+ }
+ if (is_pair) {
+ if (size == 2) {
+ segv = get_user_u32(val, addr + 4);
+ } else {
+ segv = get_user_u64(val, addr + 8);
+ }
+ if (segv) {
+ env->cp15.c6_data = addr + (size == 2 ? 4 : 8);
+ goto error;
+ }
+ if (val != env->exclusive_high) {
+ goto finish;
+ }
+ }
+ val = env->xregs[rt];
+ switch (size) {
+ case 0:
+ segv = put_user_u8(val, addr);
+ break;
+ case 1:
+ segv = put_user_u16(val, addr);
+ break;
+ case 2:
+ segv = put_user_u32(val, addr);
+ break;
+ case 3:
+ segv = put_user_u64(val, addr);
+ break;
+ }
+ if (segv) {
+ goto error;
+ }
+ if (is_pair) {
+ val = env->xregs[rt2];
+ if (size == 2) {
+ segv = put_user_u32(val, addr + 4);
+ } else {
+ segv = put_user_u64(val, addr + 8);
+ }
+ if (segv) {
+ env->cp15.c6_data = addr + (size == 2 ? 4 : 8);
+ goto error;
+ }
+ }
+ rc = 0;
+finish:
+ env->pc += 4;
+ /* rs == 31 encodes a write to the ZR, thus throwing away
+ * the status return. This is rather silly but valid.
+ */
+ if (rs < 31) {
+ env->xregs[rs] = rc;
+ }
+error:
+ /* instruction faulted, PC does not advance */
+ /* either way a strex releases any exclusive lock we have */
+ env->exclusive_addr = -1;
+ end_exclusive();
+ return segv;
+}
+
/* AArch64 main loop */
void cpu_loop(CPUARMState *env)
{
@@ -939,7 +1059,7 @@ void cpu_loop(CPUARMState *env)
}
break;
case EXCP_STREX:
- if (do_strex(env)) {
+ if (do_strex_a64(env)) {
addr = env->cp15.c6_data;
goto do_segv;
}
@@ -951,6 +1071,12 @@ void cpu_loop(CPUARMState *env)
abort();
}
process_pending_signals(env);
+ /* Exception return on AArch64 always clears the exclusive monitor,
+ * so any return to running guest code implies this.
+ * A strex (successful or otherwise) also clears the monitor, so
+ * we don't need to specialcase EXCP_STREX.
+ */
+ env->exclusive_addr = -1;
}
}
#endif /* ndef TARGET_ABI32 */
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index e2717e0775..c2f74f33d6 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -178,10 +178,8 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
struct target_pt_regs * regs, struct image_info *infop,
struct linux_binprm *);
-int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
- struct image_info * info);
-int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
- struct image_info * info);
+int load_elf_binary(struct linux_binprm *bprm, struct image_info *info);
+int load_flt_binary(struct linux_binprm *bprm, struct image_info *info);
abi_long memcpy_to_target(abi_ulong dest, const void *src,
unsigned long len);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 7751c47ef1..01d7c393df 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -1171,7 +1171,7 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
}
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
- __put_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
+ __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
__put_user(/*current->thread.fault_address*/ 0,
&sf->uc.tuc_mcontext.fault_address);
@@ -1189,8 +1189,8 @@ static int target_setup_sigframe(struct target_rt_sigframe *sf,
__put_user(env->vfp.regs[i * 2 + 1], &aux->fpsimd.vregs[i * 2 + 1]);
#endif
}
- __put_user(/*env->fpsr*/0, &aux->fpsimd.fpsr);
- __put_user(/*env->fpcr*/0, &aux->fpsimd.fpcr);
+ __put_user(vfp_get_fpsr(env), &aux->fpsimd.fpsr);
+ __put_user(vfp_get_fpcr(env), &aux->fpsimd.fpcr);
__put_user(TARGET_FPSIMD_MAGIC, &aux->fpsimd.head.magic);
__put_user(sizeof(struct target_fpsimd_context),
&aux->fpsimd.head.size);
@@ -1209,7 +1209,8 @@ static int target_restore_sigframe(CPUARMState *env,
int i;
struct target_aux_context *aux =
(struct target_aux_context *)sf->uc.tuc_mcontext.__reserved;
- uint32_t magic, size;
+ uint32_t magic, size, fpsr, fpcr;
+ uint64_t pstate;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
sigprocmask(SIG_SETMASK, &set, NULL);
@@ -1220,7 +1221,8 @@ static int target_restore_sigframe(CPUARMState *env,
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
- __get_user(env->pstate, &sf->uc.tuc_mcontext.pstate);
+ __get_user(pstate, &sf->uc.tuc_mcontext.pstate);
+ pstate_write(env, pstate);
__get_user(magic, &aux->fpsimd.head.magic);
__get_user(size, &aux->fpsimd.head.size);
@@ -1233,6 +1235,10 @@ static int target_restore_sigframe(CPUARMState *env,
for (i = 0; i < 32 * 2; i++) {
__get_user(env->vfp.regs[i], &aux->fpsimd.vregs[i]);
}
+ __get_user(fpsr, &aux->fpsimd.fpsr);
+ vfp_set_fpsr(env, fpsr);
+ __get_user(fpcr, &aux->fpsimd.fpcr);
+ vfp_set_fpcr(env, fpcr);
return 0;
}
@@ -2537,9 +2543,9 @@ void sparc64_set_context(CPUSPARCState *env)
abi_ulong *src, *dst;
src = ucp->tuc_sigmask.sig;
dst = target_set.sig;
- for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong);
- i++, dst++, src++)
+ for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) {
err |= __get_user(*dst, src);
+ }
if (err)
goto do_sigsegv;
}
@@ -2642,9 +2648,9 @@ void sparc64_get_context(CPUSPARCState *env)
abi_ulong *src, *dst;
src = target_set.sig;
dst = ucp->tuc_sigmask.sig;
- for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong);
- i++, dst++, src++)
+ for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) {
err |= __put_user(*src, dst);
+ }
if (err)
goto do_sigsegv;
}
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index efd1453987..0ac05b85f2 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2245,6 +2245,22 @@ static abi_long do_socketcall(int num, abi_ulong vptr)
ret = do_accept4(sockfd, target_addr, target_addrlen, 0);
}
break;
+ case SOCKOP_accept4:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr, target_addrlen;
+ abi_ulong flags;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(target_addrlen, vptr + 2 * n)
+ || get_user_ual(flags, vptr + 3 * n)) {
+ return -TARGET_EFAULT;
+ }
+
+ ret = do_accept4(sockfd, target_addr, target_addrlen, flags);
+ }
+ break;
case SOCKOP_getsockname:
{
abi_ulong sockfd;
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index cf08db5a23..ae30476217 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -27,6 +27,7 @@
#define SOCKOP_getsockopt 15
#define SOCKOP_sendmsg 16
#define SOCKOP_recvmsg 17
+#define SOCKOP_accept4 18
#define IPCOP_semop 1
#define IPCOP_semget 2
diff --git a/memory.c b/memory.c
index 28f64491d0..59ecc28401 100644
--- a/memory.c
+++ b/memory.c
@@ -22,6 +22,7 @@
#include <assert.h>
#include "exec/memory-internal.h"
+#include "exec/ram_addr.h"
//#define DEBUG_UNASSIGNED
@@ -1174,15 +1175,14 @@ bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client)
{
assert(mr->terminates);
- return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
- 1 << client);
+ 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);
- return cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size, -1);
+ cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size);
}
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
@@ -1190,12 +1190,9 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
{
bool ret;
assert(mr->terminates);
- ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size,
- 1 << client);
+ ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client);
if (ret) {
- cpu_physical_memory_reset_dirty(mr->ram_addr + addr,
- mr->ram_addr + addr + size,
- 1 << client);
+ cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
}
return ret;
}
@@ -1241,9 +1238,7 @@ 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,
- mr->ram_addr + addr + size,
- 1 << client);
+ cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
}
void *memory_region_get_ram_ptr(MemoryRegion *mr)
@@ -1596,6 +1591,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
view = address_space_get_flatview(as);
fr = flatview_lookup(view, range);
if (!fr) {
+ flatview_unref(view);
return ret;
}
diff --git a/migration.c b/migration.c
index 2b1ab20c54..7235c23ffe 100644
--- a/migration.c
+++ b/migration.c
@@ -40,6 +40,7 @@ enum {
MIG_STATE_ERROR = -1,
MIG_STATE_NONE,
MIG_STATE_SETUP,
+ MIG_STATE_CANCELLING,
MIG_STATE_CANCELLED,
MIG_STATE_ACTIVE,
MIG_STATE_COMPLETED,
@@ -196,6 +197,7 @@ MigrationInfo *qmp_query_migrate(Error **errp)
info->has_total_time = false;
break;
case MIG_STATE_ACTIVE:
+ case MIG_STATE_CANCELLING:
info->has_status = true;
info->status = g_strdup("active");
info->has_total_time = true;
@@ -282,6 +284,13 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params,
/* shared migration helpers */
+static void migrate_set_state(MigrationState *s, int old_state, int new_state)
+{
+ if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) {
+ trace_migrate_set_state(new_state);
+ }
+}
+
static void migrate_fd_cleanup(void *opaque)
{
MigrationState *s = opaque;
@@ -303,18 +312,14 @@ static void migrate_fd_cleanup(void *opaque)
if (s->state != MIG_STATE_COMPLETED) {
qemu_savevm_state_cancel();
+ if (s->state == MIG_STATE_CANCELLING) {
+ migrate_set_state(s, MIG_STATE_CANCELLING, MIG_STATE_CANCELLED);
+ }
}
notifier_list_notify(&migration_state_notifiers, s);
}
-static void migrate_set_state(MigrationState *s, int old_state, int new_state)
-{
- if (atomic_cmpxchg(&s->state, old_state, new_state) == new_state) {
- trace_migrate_set_state(new_state);
- }
-}
-
void migrate_fd_error(MigrationState *s)
{
DPRINTF("setting error state\n");
@@ -326,9 +331,16 @@ void migrate_fd_error(MigrationState *s)
static void migrate_fd_cancel(MigrationState *s)
{
+ int old_state ;
DPRINTF("cancelling migration\n");
- migrate_set_state(s, s->state, MIG_STATE_CANCELLED);
+ do {
+ old_state = s->state;
+ if (old_state != MIG_STATE_SETUP && old_state != MIG_STATE_ACTIVE) {
+ break;
+ }
+ migrate_set_state(s, old_state, MIG_STATE_CANCELLING);
+ } while (s->state != MIG_STATE_CANCELLING);
}
void add_migration_state_change_notifier(Notifier *notify)
@@ -405,7 +417,8 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
params.blk = has_blk && blk;
params.shared = has_inc && inc;
- if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP) {
+ if (s->state == MIG_STATE_ACTIVE || s->state == MIG_STATE_SETUP ||
+ s->state == MIG_STATE_CANCELLING) {
error_set(errp, QERR_MIGRATION_ACTIVE);
return;
}
@@ -437,6 +450,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk,
#endif
} else {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol");
+ s->state = MIG_STATE_ERROR;
return;
}
@@ -583,7 +597,7 @@ static void *migration_thread(void *opaque)
ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE);
if (ret >= 0) {
- qemu_file_set_rate_limit(s->file, INT_MAX);
+ qemu_file_set_rate_limit(s->file, INT64_MAX);
qemu_savevm_state_complete(s->file);
}
qemu_mutex_unlock_iothread();
diff --git a/monitor.c b/monitor.c
index 845f608665..80456fbe5b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -37,7 +37,7 @@
#include "ui/qemu-spice.h"
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
-#include "monitor/readline.h"
+#include "qemu/readline.h"
#include "ui/console.h"
#include "sysemu/blockdev.h"
#include "audio/audio.h"
@@ -217,8 +217,8 @@ static const mon_cmd_t qmp_cmds[];
Monitor *cur_mon;
Monitor *default_mon;
-static void monitor_command_cb(Monitor *mon, const char *cmdline,
- void *opaque);
+static void monitor_command_cb(void *opaque, const char *cmdline,
+ void *readline_opaque);
static inline int qmp_cmd_mode(const Monitor *mon)
{
@@ -4338,9 +4338,10 @@ static void monitor_find_completion_by_table(Monitor *mon,
}
}
-static void monitor_find_completion(Monitor *mon,
+static void monitor_find_completion(void *opaque,
const char *cmdline)
{
+ Monitor *mon = opaque;
char *args[MAX_ARGS];
int nb_args, len;
@@ -4751,8 +4752,11 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
cur_mon = old_mon;
}
-static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
+static void monitor_command_cb(void *opaque, const char *cmdline,
+ void *readline_opaque)
{
+ Monitor *mon = opaque;
+
monitor_suspend(mon);
handle_user_command(mon, cmdline);
monitor_resume(mon);
@@ -4881,6 +4885,22 @@ static void sortcmdlist(void)
* End:
*/
+/* These functions just adapt the readline interface in a typesafe way. We
+ * could cast function pointers but that discards compiler checks.
+ */
+static void monitor_readline_printf(void *opaque, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf(opaque, fmt, ap);
+ va_end(ap);
+}
+
+static void monitor_readline_flush(void *opaque)
+{
+ monitor_flush(opaque);
+}
+
void monitor_init(CharDriverState *chr, int flags)
{
static int is_first_init = 1;
@@ -4898,7 +4918,10 @@ void monitor_init(CharDriverState *chr, int flags)
mon->chr = chr;
mon->flags = flags;
if (flags & MONITOR_USE_READLINE) {
- mon->rs = readline_init(mon, monitor_find_completion);
+ mon->rs = readline_init(monitor_readline_printf,
+ monitor_readline_flush,
+ mon,
+ monitor_find_completion);
monitor_read_command(mon, 0);
}
@@ -4920,9 +4943,11 @@ void monitor_init(CharDriverState *chr, int flags)
default_mon = mon;
}
-static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
+static void bdrv_password_cb(void *opaque, const char *password,
+ void *readline_opaque)
{
- BlockDriverState *bs = opaque;
+ Monitor *mon = opaque;
+ BlockDriverState *bs = readline_opaque;
int ret = 0;
if (bdrv_set_key(bs, password) != 0) {
diff --git a/nbd.c b/nbd.c
index f847940f3e..030f56b5c7 100644
--- a/nbd.c
+++ b/nbd.c
@@ -443,7 +443,6 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
TRACE("Receiving negotiation.");
- qemu_set_block(csock);
rc = -EINVAL;
if (read_sync(csock, buf, 8) != 8) {
@@ -558,7 +557,6 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
rc = 0;
fail:
- qemu_set_nonblock(csock);
return rc;
}
diff --git a/net/net.c b/net/net.c
index 9db88cc0ee..f8db85f30b 100644
--- a/net/net.c
+++ b/net/net.c
@@ -856,7 +856,7 @@ static int net_host_check_device(const char *device)
,"vde"
#endif
};
- for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
+ for (i = 0; i < ARRAY_SIZE(valid_param_list); i++) {
if (!strncmp(valid_param_list[i], device,
strlen(valid_param_list[i])))
return 1;
diff --git a/qapi-schema.json b/qapi-schema.json
index d6f8615942..05ced9d572 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -810,6 +810,8 @@
#
# @file: the filename of the backing device
#
+# @node-name: #optional the name of the block driver node (Since 2.0)
+#
# @ro: true if the backing device was open read-only
#
# @drv: the name of the block format used to open the backing device. As of
@@ -857,10 +859,9 @@
#
# Since: 0.14.0
#
-# Notes: This interface is only found in @BlockInfo.
##
{ 'type': 'BlockDeviceInfo',
- 'data': { 'file': 'str', 'ro': 'bool', 'drv': 'str',
+ 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
'*backing_file': 'str', 'backing_file_depth': 'int',
'encrypted': 'bool', 'encryption_key_missing': 'bool',
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
@@ -949,7 +950,7 @@
# (only present if removable is true)
#
# @dirty-bitmaps: #optional dirty bitmaps information (only present if the
-# driver has one or more dirty bitmaps) (Since 1.8)
+# driver has one or more dirty bitmaps) (Since 2.0)
#
# @io-status: #optional @BlockDeviceIoStatus. Only present if the device
# supports it and the VM is configured to stop on errors
@@ -1022,15 +1023,17 @@
#
# @stats: A @BlockDeviceStats for the device.
#
-# @parent: #optional This may point to the backing block device if this is a
-# a virtual block device. If it's a backing block, this will point
-# to the backing file is one is present.
+# @parent: #optional This describes the file block device if it has one.
+#
+# @backing: #optional This describes the backing block device if it has one.
+# (Since 2.0)
#
# Since: 0.14.0
##
{ 'type': 'BlockStats',
'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
- '*parent': 'BlockStats'} }
+ '*parent': 'BlockStats',
+ '*backing': 'BlockStats'} }
##
# @query-blockstats:
@@ -1675,7 +1678,11 @@
# determine which ones are encrypted, set the passwords with this command, and
# then start the guest with the @cont command.
#
-# @device: the name of the device to set the password on
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the block backend device to set the password on
+#
+# @node-name: #optional graph node name to set the password on (Since 2.0)
#
# @password: the password to use for the device
#
@@ -1689,7 +1696,8 @@
#
# Since: 0.14.0
##
-{ 'command': 'block_passwd', 'data': {'device': 'str', 'password': 'str'} }
+{ 'command': 'block_passwd', 'data': {'*device': 'str',
+ '*node-name': 'str', 'password': 'str'} }
##
# @balloon:
@@ -1716,7 +1724,11 @@
#
# Resize a block image while a guest is running.
#
-# @device: the name of the device to get the image resized
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the device to get the image resized
+#
+# @node-name: #optional graph node name to get the image resized (Since 2.0)
#
# @size: new image size in bytes
#
@@ -1725,7 +1737,9 @@
#
# Since: 0.14.0
##
-{ 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }}
+{ 'command': 'block_resize', 'data': { '*device': 'str',
+ '*node-name': 'str',
+ 'size': 'int' }}
##
# @NewImageMode
@@ -1747,18 +1761,25 @@
##
# @BlockdevSnapshot
#
-# @device: the name of the device to generate the snapshot from.
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the device to generate the snapshot from.
+#
+# @node-name: #optional graph node name to generate the snapshot from (Since 2.0)
#
# @snapshot-file: the target of the new image. A new file will be created.
#
+# @snapshot-node-name: #optional the graph node name of the new image (Since 2.0)
+#
# @format: #optional the format of the snapshot image, default is 'qcow2'.
#
# @mode: #optional whether and how QEMU should create a new image, default is
# 'absolute-paths'.
##
{ 'type': 'BlockdevSnapshot',
- 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
- '*mode': 'NewImageMode' } }
+ 'data': { '*device': 'str', '*node-name': 'str',
+ 'snapshot-file': 'str', '*snapshot-node-name': 'str',
+ '*format': 'str', '*mode': 'NewImageMode' } }
##
# @BlockdevSnapshotInternal
@@ -1967,9 +1988,18 @@
#
# @top: The file name of the backing image within the image chain,
# which contains the topmost data to be committed down.
-# Note, the active layer as 'top' is currently unsupported.
#
# If top == base, that is an error.
+# If top == active, the job will not be completed by itself,
+# user needs to complete the job with the block-job-complete
+# command after getting the ready event. (Since 2.0)
+#
+# If the base image is smaller than top, then the base image
+# will be resized to be the same size as top. If top is
+# smaller than the base image, the base will not be
+# truncated. If you want the base image size to match the
+# size of the smaller top, you can safely truncate it
+# yourself once the commit operation successfully completes.
#
#
# @speed: #optional the maximum speed, in bytes per second
@@ -1979,7 +2009,6 @@
# If @device does not exist, DeviceNotFound
# If image commit is not supported by this device, NotSupported
# If @base or @top is invalid, a generic error is returned
-# If @top is the active layer, or omitted, a generic error is returned
# If @speed is invalid, InvalidParameter
#
# Since: 1.3
@@ -2008,6 +2037,17 @@
{ 'command': 'drive-backup', 'data': 'DriveBackup' }
##
+# @query-named-block-nodes
+#
+# Get the named block driver list
+#
+# Returns: the list of BlockDeviceInfo
+#
+# Since 2.0
+##
+{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
+
+##
# @drive-mirror
#
# Start mirroring a block device's writes to a new destination.
@@ -2759,6 +2799,40 @@
{ 'command': 'netdev_del', 'data': {'id': 'str'} }
##
+# @object-add:
+#
+# Create a QOM object.
+#
+# @qom-type: the class name for the object to be created
+#
+# @id: the name of the new object
+#
+# @props: #optional a dictionary of properties to be passed to the backend
+#
+# Returns: Nothing on success
+# Error if @qom-type is not a valid class name
+#
+# Since: 2.0
+##
+{ 'command': 'object-add',
+ 'data': {'qom-type': 'str', 'id': 'str', '*props': 'dict'},
+ 'gen': 'no' }
+
+##
+# @object-del:
+#
+# Remove a QOM object.
+#
+# @id: the name of the QOM object to remove
+#
+# Returns: Nothing on success
+# Error if @id is not a valid id for a QOM object
+#
+# Since: 2.0
+##
+{ 'command': 'object-del', 'data': {'id': 'str'} }
+
+##
# @NetdevNoneOptions
#
# Use it alone to have zero network devices.
@@ -3022,7 +3096,7 @@
#
# @devname: #optional path of the netmap device (default: '/dev/netmap').
#
-# Since 1.8
+# Since 2.0
##
{ 'type': 'NetdevNetmapOptions',
'data': {
@@ -4055,6 +4129,7 @@
# @id: #optional id by which the new block device can be referred to.
# This is a required option on the top level of blockdev-add, and
# currently not allowed on any other level.
+# @node-name: #optional the name of a block driver state node (Since 2.0)
# @discard: #optional discard-related options (default: ignore)
# @cache: #optional cache-related options
# @aio: #optional AIO backend (default: threads)
@@ -4070,6 +4145,7 @@
{ 'type': 'BlockdevOptionsBase',
'data': { 'driver': 'str',
'*id': 'str',
+ '*node-name': 'str',
'*discard': 'BlockdevDiscardOptions',
'*cache': 'BlockdevCacheOptions',
'*aio': 'BlockdevAioOptions',
@@ -4166,6 +4242,116 @@
'*pass-discard-other': 'bool' } }
##
+# @BlkdebugEvent
+#
+# Trigger events supported by blkdebug.
+##
+{ 'enum': 'BlkdebugEvent',
+ 'data': [ 'l1_update', 'l1_grow.alloc_table', 'l1_grow.write_table',
+ 'l1_grow.activate_table', 'l2_load', 'l2_update',
+ 'l2_update_compressed', 'l2_alloc.cow_read', 'l2_alloc.write',
+ 'read_aio', 'read_backing_aio', 'read_compressed', 'write_aio',
+ 'write_compressed', 'vmstate_load', 'vmstate_save', 'cow_read',
+ 'cow_write', 'reftable_load', 'reftable_grow', 'reftable_update',
+ 'refblock_load', 'refblock_update', 'refblock_update_part',
+ 'refblock_alloc', 'refblock_alloc.hookup', 'refblock_alloc.write',
+ 'refblock_alloc.write_blocks', 'refblock_alloc.write_table',
+ 'refblock_alloc.switch_table', 'cluster_alloc',
+ 'cluster_alloc_bytes', 'cluster_free', 'flush_to_os',
+ 'flush_to_disk' ] }
+
+##
+# @BlkdebugInjectErrorOptions
+#
+# Describes a single error injection for blkdebug.
+#
+# @event: trigger event
+#
+# @state: #optional the state identifier blkdebug needs to be in to
+# actually trigger the event; defaults to "any"
+#
+# @errno: #optional error identifier (errno) to be returned; defaults to
+# EIO
+#
+# @sector: #optional specifies the sector index which has to be affected
+# in order to actually trigger the event; defaults to "any
+# sector"
+#
+# @once: #optional disables further events after this one has been
+# triggered; defaults to false
+#
+# @immediately: #optional fail immediately; defaults to false
+#
+# Since: 2.0
+##
+{ 'type': 'BlkdebugInjectErrorOptions',
+ 'data': { 'event': 'BlkdebugEvent',
+ '*state': 'int',
+ '*errno': 'int',
+ '*sector': 'int',
+ '*once': 'bool',
+ '*immediately': 'bool' } }
+
+##
+# @BlkdebugSetStateOptions
+#
+# Describes a single state-change event for blkdebug.
+#
+# @event: trigger event
+#
+# @state: #optional the current state identifier blkdebug needs to be in;
+# defaults to "any"
+#
+# @new_state: the state identifier blkdebug is supposed to assume if
+# this event is triggered
+#
+# Since: 2.0
+##
+{ 'type': 'BlkdebugSetStateOptions',
+ 'data': { 'event': 'BlkdebugEvent',
+ '*state': 'int',
+ 'new_state': 'int' } }
+
+##
+# @BlockdevOptionsBlkdebug
+#
+# Driver specific block device options for blkdebug.
+#
+# @image: underlying raw block device (or image file)
+#
+# @config: #optional filename of the configuration file
+#
+# @align: #optional required alignment for requests in bytes
+#
+# @inject-error: #optional array of error injection descriptions
+#
+# @set-state: #optional array of state-change descriptions
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsBlkdebug',
+ 'data': { 'image': 'BlockdevRef',
+ '*config': 'str',
+ '*align': 'int',
+ '*inject-error': ['BlkdebugInjectErrorOptions'],
+ '*set-state': ['BlkdebugSetStateOptions'] } }
+
+##
+# @BlockdevOptionsBlkverify
+#
+# Driver specific block device options for blkverify.
+#
+# @test: block device to be tested
+#
+# @raw: raw image used for verification
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsBlkverify',
+ 'data': { 'test': 'BlockdevRef',
+ 'raw': 'BlockdevRef' } }
+
+##
# @BlockdevOptions
#
# Options for creating a block device.
@@ -4189,10 +4375,8 @@
# TODO sheepdog: Wait for structured options
# TODO ssh: Should take InetSocketAddress for 'host'?
'vvfat': 'BlockdevOptionsVVFAT',
-
-# TODO blkdebug: Wait for structured options
-# TODO blkverify: Wait for structured options
-
+ 'blkdebug': 'BlockdevOptionsBlkdebug',
+ 'blkverify': 'BlockdevOptionsBlkverify',
'bochs': 'BlockdevOptionsGenericFormat',
'cloop': 'BlockdevOptionsGenericFormat',
'cow': 'BlockdevOptionsGenericCOWFormat',
diff --git a/qdev-monitor.c b/qdev-monitor.c
index dc37a43dd9..1d3b68d40a 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -87,7 +87,7 @@ static void qdev_print_devinfo(DeviceClass *dc)
if (dc->desc) {
error_printf(", desc \"%s\"", dc->desc);
}
- if (dc->no_user) {
+ if (dc->cannot_instantiate_with_device_add_yet) {
error_printf(", no-user");
}
error_printf("\n");
@@ -127,7 +127,8 @@ static void qdev_print_devinfos(bool show_no_user)
if ((i < DEVICE_CATEGORY_MAX
? !test_bit(i, dc->categories)
: !bitmap_empty(dc->categories, DEVICE_CATEGORY_MAX))
- || (!show_no_user && dc->no_user)) {
+ || (!show_no_user
+ && dc->cannot_instantiate_with_device_add_yet)) {
continue;
}
if (!cat_printed) {
@@ -477,8 +478,9 @@ DeviceState *qdev_device_add(QemuOpts *opts)
}
}
- if (!oc) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "device type");
+ if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) {
+ qerror_report(ERROR_CLASS_GENERIC_ERROR,
+ "'%s' is not a valid device model name", driver);
return NULL;
}
@@ -489,6 +491,11 @@ DeviceState *qdev_device_add(QemuOpts *opts)
}
dc = DEVICE_CLASS(oc);
+ if (dc->cannot_instantiate_with_device_add_yet) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver",
+ "pluggable device type");
+ return NULL;
+ }
/* find bus */
path = qemu_opt_get(opts, "bus");
@@ -730,7 +737,7 @@ int qemu_global_option(const char *str)
return -1;
}
- opts = qemu_opts_create_nofail(&qemu_global_opts);
+ opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
qemu_opt_set(opts, "driver", driver);
qemu_opt_set(opts, "property", property);
qemu_opt_set(opts, "value", str+offset+1);
diff --git a/qemu-char.c b/qemu-char.c
index e00f84c8e9..30c5a6afd0 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -1975,8 +1975,7 @@ static void win_stdio_wait_func(void *opaque)
DWORD dwSize;
int i;
- ret = ReadConsoleInput(stdio->hStdIn, buf, sizeof(buf) / sizeof(*buf),
- &dwSize);
+ ret = ReadConsoleInput(stdio->hStdIn, buf, ARRAY_SIZE(buf), &dwSize);
if (!ret) {
/* Avoid error storm */
@@ -3353,6 +3352,13 @@ void qemu_chr_fe_set_open(struct CharDriverState *chr, int fe_open)
}
}
+void qemu_chr_fe_event(struct CharDriverState *chr, int event)
+{
+ if (chr->chr_fe_event) {
+ chr->chr_fe_event(chr, event);
+ }
+}
+
int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
GIOFunc func, void *user_data)
{
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 185dd47a03..ce61f30d6e 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -536,11 +536,11 @@ support of multiple VM snapshots.
Supported options:
@table @code
@item compat
-Determines the qcow2 version to use. @code{compat=0.10} uses the traditional
-image format that can be read by any QEMU since 0.10 (this is the default).
+Determines the qcow2 version to use. @code{compat=0.10} uses the
+traditional image format that can be read by any QEMU since 0.10.
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
-newer understand. Amongst others, this includes zero clusters, which allow
-efficient copy-on-read for sparse images.
+newer understand (this is the default). Amongst others, this includes
+zero clusters, which allow efficient copy-on-read for sparse images.
@item backing_file
File name of a base image (see @option{create} subcommand)
@@ -654,6 +654,21 @@ Supported options:
Specifies which VHD subformat to use. Valid options are
@code{dynamic} (default) and @code{fixed}.
@end table
+
+@item VHDX
+Hyper-V compatible image format (VHDX).
+Supported options:
+@table @code
+@item subformat
+Specifies which VHDX subformat to use. Valid options are
+@code{dynamic} (default) and @code{fixed}.
+@item block_state_zero
+Force use of payload blocks of type 'ZERO'.
+@item block_size
+Block size; min 1 MB, max 256 MB. 0 means auto-calculate based on image size.
+@item log_size
+Log size; min 1 MB.
+@end table
@end table
@subsubsection Read-only formats
diff --git a/qemu-file.c b/qemu-file.c
new file mode 100644
index 0000000000..9473b674ba
--- /dev/null
+++ b/qemu-file.c
@@ -0,0 +1,826 @@
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "qemu/sockets.h"
+#include "block/coroutine.h"
+#include "migration/migration.h"
+#include "migration/qemu-file.h"
+
+#define IO_BUF_SIZE 32768
+#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
+
+struct QEMUFile {
+ const QEMUFileOps *ops;
+ void *opaque;
+
+ int64_t bytes_xfer;
+ int64_t xfer_limit;
+
+ int64_t pos; /* start of buffer when writing, end of buffer
+ when reading */
+ int buf_index;
+ int buf_size; /* 0 when writing */
+ uint8_t buf[IO_BUF_SIZE];
+
+ struct iovec iov[MAX_IOV_SIZE];
+ unsigned int iovcnt;
+
+ int last_error;
+};
+
+typedef struct QEMUFileStdio {
+ FILE *stdio_file;
+ QEMUFile *file;
+} QEMUFileStdio;
+
+typedef struct QEMUFileSocket {
+ int fd;
+ QEMUFile *file;
+} QEMUFileSocket;
+
+static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+ ssize_t size = iov_size(iov, iovcnt);
+
+ len = iov_send(s->fd, iov, iovcnt, 0, size);
+ if (len < size) {
+ len = -socket_error();
+ }
+ return len;
+}
+
+static int socket_get_fd(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+
+ return s->fd;
+}
+
+static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ for (;;) {
+ len = qemu_recv(s->fd, buf, size, 0);
+ if (len != -1) {
+ break;
+ }
+ if (socket_error() == EAGAIN) {
+ yield_until_fd_readable(s->fd);
+ } else if (socket_error() != EINTR) {
+ break;
+ }
+ }
+
+ if (len == -1) {
+ len = -socket_error();
+ }
+ return len;
+}
+
+static int socket_close(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+ closesocket(s->fd);
+ g_free(s);
+ return 0;
+}
+
+static int stdio_get_fd(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+
+ return fileno(s->stdio_file);
+}
+
+static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos,
+ int size)
+{
+ QEMUFileStdio *s = opaque;
+ return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ FILE *fp = s->stdio_file;
+ int bytes;
+
+ for (;;) {
+ clearerr(fp);
+ bytes = fread(buf, 1, size, fp);
+ if (bytes != 0 || !ferror(fp)) {
+ break;
+ }
+ if (errno == EAGAIN) {
+ yield_until_fd_readable(fileno(fp));
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
+ return bytes;
+}
+
+static int stdio_pclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ int ret;
+ ret = pclose(s->stdio_file);
+ if (ret == -1) {
+ ret = -errno;
+ } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+ /* close succeeded, but non-zero exit code: */
+ ret = -EIO; /* fake errno value */
+ }
+ g_free(s);
+ return ret;
+}
+
+static int stdio_fclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ int ret = 0;
+
+ if (s->file->ops->put_buffer || s->file->ops->writev_buffer) {
+ int fd = fileno(s->stdio_file);
+ struct stat st;
+
+ ret = fstat(fd, &st);
+ if (ret == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * If the file handle is a regular file make sure the
+ * data is flushed to disk before signaling success.
+ */
+ ret = fsync(fd);
+ if (ret != 0) {
+ ret = -errno;
+ return ret;
+ }
+ }
+ }
+ if (fclose(s->stdio_file) == EOF) {
+ ret = -errno;
+ }
+ g_free(s);
+ return ret;
+}
+
+static const QEMUFileOps stdio_pipe_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_pclose
+};
+
+static const QEMUFileOps stdio_pipe_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_pclose
+};
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+ FILE *stdio_file;
+ QEMUFileStdio *s;
+
+ if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ stdio_file = popen(command, mode);
+ if (stdio_file == NULL) {
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileStdio));
+
+ s->stdio_file = stdio_file;
+
+ if (mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
+ }
+ return s->file;
+}
+
+static const QEMUFileOps stdio_file_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_fclose
+};
+
+static const QEMUFileOps stdio_file_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_fclose
+};
+
+static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+ int64_t pos)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len, offset;
+ ssize_t size = iov_size(iov, iovcnt);
+ ssize_t total = 0;
+
+ assert(iovcnt > 0);
+ offset = 0;
+ while (size > 0) {
+ /* Find the next start position; skip all full-sized vector elements */
+ while (offset >= iov[0].iov_len) {
+ offset -= iov[0].iov_len;
+ iov++, iovcnt--;
+ }
+
+ /* skip `offset' bytes from the (now) first element, undo it on exit */
+ assert(iovcnt > 0);
+ iov[0].iov_base += offset;
+ iov[0].iov_len -= offset;
+
+ do {
+ len = writev(s->fd, iov, iovcnt);
+ } while (len == -1 && errno == EINTR);
+ if (len == -1) {
+ return -errno;
+ }
+
+ /* Undo the changes above */
+ iov[0].iov_base -= offset;
+ iov[0].iov_len += offset;
+
+ /* Prepare for the next iteration */
+ offset += len;
+ total += len;
+ size -= len;
+ }
+
+ return total;
+}
+
+static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ for (;;) {
+ len = read(s->fd, buf, size);
+ if (len != -1) {
+ break;
+ }
+ if (errno == EAGAIN) {
+ yield_until_fd_readable(s->fd);
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
+
+ if (len == -1) {
+ len = -errno;
+ }
+ return len;
+}
+
+static int unix_close(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+ close(s->fd);
+ g_free(s);
+ return 0;
+}
+
+static const QEMUFileOps unix_read_ops = {
+ .get_fd = socket_get_fd,
+ .get_buffer = unix_get_buffer,
+ .close = unix_close
+};
+
+static const QEMUFileOps unix_write_ops = {
+ .get_fd = socket_get_fd,
+ .writev_buffer = unix_writev_buffer,
+ .close = unix_close
+};
+
+QEMUFile *qemu_fdopen(int fd, const char *mode)
+{
+ QEMUFileSocket *s;
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileSocket));
+ s->fd = fd;
+
+ if (mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, &unix_read_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &unix_write_ops);
+ }
+ return s->file;
+}
+
+static const QEMUFileOps socket_read_ops = {
+ .get_fd = socket_get_fd,
+ .get_buffer = socket_get_buffer,
+ .close = socket_close
+};
+
+static const QEMUFileOps socket_write_ops = {
+ .get_fd = socket_get_fd,
+ .writev_buffer = socket_writev_buffer,
+ .close = socket_close
+};
+
+bool qemu_file_mode_is_not_valid(const char *mode)
+{
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+ return true;
+ }
+
+ return false;
+}
+
+QEMUFile *qemu_fopen_socket(int fd, const char *mode)
+{
+ QEMUFileSocket *s;
+
+ if (qemu_file_mode_is_not_valid(mode)) {
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileSocket));
+ s->fd = fd;
+ if (mode[0] == 'w') {
+ qemu_set_block(s->fd);
+ s->file = qemu_fopen_ops(s, &socket_write_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &socket_read_ops);
+ }
+ return s->file;
+}
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (qemu_file_mode_is_not_valid(mode)) {
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileStdio));
+
+ s->stdio_file = fopen(filename, mode);
+ if (!s->stdio_file) {
+ goto fail;
+ }
+
+ if (mode[0] == 'w') {
+ s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+ }
+ return s->file;
+fail:
+ g_free(s);
+ return NULL;
+}
+
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
+{
+ QEMUFile *f;
+
+ f = g_malloc0(sizeof(QEMUFile));
+
+ f->opaque = opaque;
+ f->ops = ops;
+ return f;
+}
+
+/*
+ * Get last error for stream f
+ *
+ * Return negative error value if there has been an error on previous
+ * operations, return 0 if no error happened.
+ *
+ */
+int qemu_file_get_error(QEMUFile *f)
+{
+ return f->last_error;
+}
+
+void qemu_file_set_error(QEMUFile *f, int ret)
+{
+ if (f->last_error == 0) {
+ f->last_error = ret;
+ }
+}
+
+static inline bool qemu_file_is_writable(QEMUFile *f)
+{
+ return f->ops->writev_buffer || f->ops->put_buffer;
+}
+
+/**
+ * Flushes QEMUFile buffer
+ *
+ * If there is writev_buffer QEMUFileOps it uses it otherwise uses
+ * put_buffer ops.
+ */
+void qemu_fflush(QEMUFile *f)
+{
+ ssize_t ret = 0;
+
+ if (!qemu_file_is_writable(f)) {
+ return;
+ }
+
+ if (f->ops->writev_buffer) {
+ if (f->iovcnt > 0) {
+ ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
+ }
+ } else {
+ if (f->buf_index > 0) {
+ ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
+ }
+ }
+ if (ret >= 0) {
+ f->pos += ret;
+ }
+ f->buf_index = 0;
+ f->iovcnt = 0;
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+}
+
+void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
+{
+ int ret = 0;
+
+ if (f->ops->before_ram_iterate) {
+ ret = f->ops->before_ram_iterate(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+}
+
+void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
+{
+ int ret = 0;
+
+ if (f->ops->after_ram_iterate) {
+ ret = f->ops->after_ram_iterate(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+}
+
+void ram_control_load_hook(QEMUFile *f, uint64_t flags)
+{
+ int ret = -EINVAL;
+
+ if (f->ops->hook_ram_load) {
+ ret = f->ops->hook_ram_load(f, f->opaque, flags);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ } else {
+ qemu_file_set_error(f, ret);
+ }
+}
+
+size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
+ ram_addr_t offset, size_t size, int *bytes_sent)
+{
+ if (f->ops->save_page) {
+ int ret = f->ops->save_page(f, f->opaque, block_offset,
+ offset, size, bytes_sent);
+
+ if (ret != RAM_SAVE_CONTROL_DELAYED) {
+ if (bytes_sent && *bytes_sent > 0) {
+ qemu_update_position(f, *bytes_sent);
+ } else if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+ }
+
+ return ret;
+ }
+
+ return RAM_SAVE_CONTROL_NOT_SUPP;
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+ int len;
+ int pending;
+
+ assert(!qemu_file_is_writable(f));
+
+ pending = f->buf_size - f->buf_index;
+ if (pending > 0) {
+ memmove(f->buf, f->buf + f->buf_index, pending);
+ }
+ f->buf_index = 0;
+ f->buf_size = pending;
+
+ len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
+ IO_BUF_SIZE - pending);
+ if (len > 0) {
+ f->buf_size += len;
+ f->pos += len;
+ } else if (len == 0) {
+ qemu_file_set_error(f, -EIO);
+ } else if (len != -EAGAIN) {
+ qemu_file_set_error(f, len);
+ }
+}
+
+int qemu_get_fd(QEMUFile *f)
+{
+ if (f->ops->get_fd) {
+ return f->ops->get_fd(f->opaque);
+ }
+ return -1;
+}
+
+void qemu_update_position(QEMUFile *f, size_t size)
+{
+ f->pos += size;
+}
+
+/** Closes the file
+ *
+ * Returns negative error value if any error happened on previous operations or
+ * while closing the file. Returns 0 or positive number on success.
+ *
+ * The meaning of return value on success depends on the specific backend
+ * being used.
+ */
+int qemu_fclose(QEMUFile *f)
+{
+ int ret;
+ qemu_fflush(f);
+ ret = qemu_file_get_error(f);
+
+ if (f->ops->close) {
+ int ret2 = f->ops->close(f->opaque);
+ if (ret >= 0) {
+ ret = ret2;
+ }
+ }
+ /* If any error was spotted before closing, we should report it
+ * instead of the close() return value.
+ */
+ if (f->last_error) {
+ ret = f->last_error;
+ }
+ g_free(f);
+ return ret;
+}
+
+static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
+{
+ /* check for adjacent buffer and coalesce them */
+ if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
+ f->iov[f->iovcnt - 1].iov_len) {
+ f->iov[f->iovcnt - 1].iov_len += size;
+ } else {
+ f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
+ f->iov[f->iovcnt++].iov_len = size;
+ }
+
+ if (f->iovcnt >= MAX_IOV_SIZE) {
+ qemu_fflush(f);
+ }
+}
+
+void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
+{
+ if (!f->ops->writev_buffer) {
+ qemu_put_buffer(f, buf, size);
+ return;
+ }
+
+ if (f->last_error) {
+ return;
+ }
+
+ f->bytes_xfer += size;
+ add_to_iovec(f, buf, size);
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ int l;
+
+ if (f->last_error) {
+ return;
+ }
+
+ while (size > 0) {
+ l = IO_BUF_SIZE - f->buf_index;
+ if (l > size) {
+ l = size;
+ }
+ memcpy(f->buf + f->buf_index, buf, l);
+ f->bytes_xfer += l;
+ if (f->ops->writev_buffer) {
+ add_to_iovec(f, f->buf + f->buf_index, l);
+ }
+ f->buf_index += l;
+ if (f->buf_index == IO_BUF_SIZE) {
+ qemu_fflush(f);
+ }
+ if (qemu_file_get_error(f)) {
+ break;
+ }
+ buf += l;
+ size -= l;
+ }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+ if (f->last_error) {
+ return;
+ }
+
+ f->buf[f->buf_index] = v;
+ f->bytes_xfer++;
+ if (f->ops->writev_buffer) {
+ add_to_iovec(f, f->buf + f->buf_index, 1);
+ }
+ f->buf_index++;
+ if (f->buf_index == IO_BUF_SIZE) {
+ qemu_fflush(f);
+ }
+}
+
+void qemu_file_skip(QEMUFile *f, int size)
+{
+ if (f->buf_index + size <= f->buf_size) {
+ f->buf_index += size;
+ }
+}
+
+int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+ int pending;
+ int index;
+
+ assert(!qemu_file_is_writable(f));
+
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ if (pending < size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ }
+
+ if (pending <= 0) {
+ return 0;
+ }
+ if (size > pending) {
+ size = pending;
+ }
+
+ memcpy(buf, f->buf + index, size);
+ return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ int pending = size;
+ int done = 0;
+
+ while (pending > 0) {
+ int res;
+
+ res = qemu_peek_buffer(f, buf, pending, 0);
+ if (res == 0) {
+ return done;
+ }
+ qemu_file_skip(f, res);
+ buf += res;
+ pending -= res;
+ done += res;
+ }
+ return done;
+}
+
+int qemu_peek_byte(QEMUFile *f, int offset)
+{
+ int index = f->buf_index + offset;
+
+ assert(!qemu_file_is_writable(f));
+
+ if (index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ if (index >= f->buf_size) {
+ return 0;
+ }
+ }
+ return f->buf[index];
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+ int result;
+
+ result = qemu_peek_byte(f, 0);
+ qemu_file_skip(f, 1);
+ return result;
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+ qemu_fflush(f);
+ return f->pos;
+}
+
+int qemu_file_rate_limit(QEMUFile *f)
+{
+ if (qemu_file_get_error(f)) {
+ return 1;
+ }
+ if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t qemu_file_get_rate_limit(QEMUFile *f)
+{
+ return f->xfer_limit;
+}
+
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
+{
+ f->xfer_limit = limit;
+}
+
+void qemu_file_reset_rate_limit(QEMUFile *f)
+{
+ f->bytes_xfer = 0;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 24);
+ qemu_put_byte(f, v >> 16);
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+ qemu_put_be32(f, v >> 32);
+ qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 24;
+ v |= qemu_get_byte(f) << 16;
+ v |= qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+ uint64_t v;
+ v = (uint64_t)qemu_get_be32(f) << 32;
+ v |= qemu_get_be32(f);
+ return v;
+}
diff --git a/qemu-img.c b/qemu-img.c
index 7dfe982b0c..c989850ce7 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1135,8 +1135,7 @@ static int img_convert(int argc, char **argv)
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
BlockDriver *drv, *proto_drv;
BlockDriverState **bs = NULL, *out_bs = NULL;
- int64_t total_sectors, nb_sectors, sector_num, bs_offset,
- sector_num_next_status = 0;
+ int64_t total_sectors, nb_sectors, sector_num, bs_offset;
uint64_t bs_sectors;
uint8_t * buf = NULL;
size_t bufsectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE;
@@ -1505,6 +1504,8 @@ static int img_convert(int argc, char **argv)
/* signal EOF to align */
bdrv_write_compressed(out_bs, 0, NULL, 0);
} else {
+ int64_t sectors_to_read, sectors_read, sector_num_next_status;
+ bool count_allocated_sectors;
int has_zero_init = min_sparse ? bdrv_has_zero_init(out_bs) : 0;
if (!has_zero_init && bdrv_can_write_zeroes_with_unmap(out_bs)) {
@@ -1515,12 +1516,21 @@ static int img_convert(int argc, char **argv)
has_zero_init = 1;
}
+ sectors_to_read = total_sectors;
+ count_allocated_sectors = progress && (out_baseimg || has_zero_init);
+restart:
sector_num = 0; // total number of sectors converted so far
- nb_sectors = total_sectors - sector_num;
+ sectors_read = 0;
+ sector_num_next_status = 0;
for(;;) {
nb_sectors = total_sectors - sector_num;
if (nb_sectors <= 0) {
+ if (count_allocated_sectors) {
+ sectors_to_read = sectors_read;
+ count_allocated_sectors = false;
+ goto restart;
+ }
ret = 0;
break;
}
@@ -1586,8 +1596,14 @@ static int img_convert(int argc, char **argv)
}
n = MIN(n, bs_sectors - (sector_num - bs_offset));
- n1 = n;
+ sectors_read += n;
+ if (count_allocated_sectors) {
+ sector_num += n;
+ continue;
+ }
+
+ n1 = n;
ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
if (ret < 0) {
error_report("error while reading sector %" PRId64 ": %s",
@@ -1612,7 +1628,7 @@ static int img_convert(int argc, char **argv)
n -= n1;
buf1 += n1 * 512;
}
- qemu_progress_print(100.0 * sector_num / total_sectors, 0);
+ qemu_progress_print(100.0 * sectors_read / sectors_to_read, 0);
}
}
out:
@@ -2548,7 +2564,7 @@ static int img_resize(int argc, char **argv)
}
/* Parse size */
- param = qemu_opts_create_nofail(&resize_options);
+ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort);
if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) {
/* Error message already printed when size parsing fails */
ret = -1;
diff --git a/qemu-img.texi b/qemu-img.texi
index be31191e43..526d56a458 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -57,7 +57,9 @@ indicates that target image must be compressed (qcow format only)
@item -h
with or without a command shows help and lists the supported formats
@item -p
-display progress bar (convert and rebase commands only)
+display progress bar (compare, convert and rebase commands only).
+If the @var{-p} option is not used for a command that supports it, the
+progress is reported when the process receives a @code{SIGUSR1} signal.
@item -q
Quiet mode - do not print any output (except errors). There's no progress bar
in case both @var{-q} and @var{-p} options are used.
@@ -140,7 +142,12 @@ it doesn't need to be specified separately in this case.
@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
-Commit the changes recorded in @var{filename} in its base image.
+Commit the changes recorded in @var{filename} in its base image or backing file.
+If the backing file is smaller than the snapshot, then the backing file will be
+resized to be the same size as the snapshot. If the snapshot is smaller than
+the backing file, the backing file will not be truncated. If you want the
+backing file to match the size of the smaller snapshot, you can safely truncate
+it yourself once the commit operation successfully completes.
@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-s] [-q] @var{filename1} @var{filename2}
@@ -391,11 +398,11 @@ support of multiple VM snapshots.
Supported options:
@table @code
@item compat
-Determines the qcow2 version to use. @code{compat=0.10} uses the traditional
-image format that can be read by any QEMU since 0.10 (this is the default).
+Determines the qcow2 version to use. @code{compat=0.10} uses the
+traditional image format that can be read by any QEMU since 0.10.
@code{compat=1.1} enables image format extensions that only QEMU 1.1 and
-newer understand. Amongst others, this includes zero clusters, which allow
-efficient copy-on-read for sparse images.
+newer understand (this is the default). Amongst others, this includes zero
+clusters, which allow efficient copy-on-read for sparse images.
@item backing_file
File name of a base image (see @option{create} subcommand)
@@ -431,8 +438,8 @@ This option can only be enabled if @code{compat=1.1} is specified.
@item Other
QEMU also supports various other image file formats for compatibility with
-older QEMU versions or other hypervisors, including VMDK, VDI, VHD (vpc), qcow1
-and QED. For a full list of supported formats see @code{qemu-img --help}.
+older QEMU versions or other hypervisors, including VMDK, VDI, VHD (vpc), VHDX,
+qcow1 and QED. For a full list of supported formats see @code{qemu-img --help}.
For a more detailed description of these formats, see the QEMU Emulation User
Documentation.
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 85e4982bd8..f1de24c91c 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -12,6 +12,7 @@
#include "block/block_int.h"
#include "block/qapi.h"
#include "qemu/main-loop.h"
+#include "qemu/timer.h"
#define CMD_NOFILE_OK 0x01
@@ -94,6 +95,21 @@ static const cmdinfo_t *find_command(const char *cmd)
return NULL;
}
+/* Invoke fn() for commands with a matching prefix */
+void qemuio_complete_command(const char *input,
+ void (*fn)(const char *cmd, void *opaque),
+ void *opaque)
+{
+ cmdinfo_t *ct;
+ size_t input_len = strlen(input);
+
+ for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
+ if (strncmp(input, ct->name, input_len) == 0) {
+ fn(ct->name, opaque);
+ }
+ }
+}
+
static char **breakline(char *input, int *count)
{
int c = 0;
@@ -2038,6 +2054,46 @@ static const cmdinfo_t abort_cmd = {
.oneline = "simulate a program crash using abort(3)",
};
+static void sleep_cb(void *opaque)
+{
+ bool *expired = opaque;
+ *expired = true;
+}
+
+static int sleep_f(BlockDriverState *bs, int argc, char **argv)
+{
+ char *endptr;
+ long ms;
+ struct QEMUTimer *timer;
+ bool expired = false;
+
+ ms = strtol(argv[1], &endptr, 0);
+ if (ms < 0 || *endptr != '\0') {
+ printf("%s is not a valid number\n", argv[1]);
+ return 0;
+ }
+
+ timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired);
+ timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_HOST) + SCALE_MS * ms);
+
+ while (!expired) {
+ main_loop_wait(false);
+ }
+
+ timer_free(timer);
+
+ return 0;
+}
+
+static const cmdinfo_t sleep_cmd = {
+ .name = "sleep",
+ .argmin = 1,
+ .argmax = 1,
+ .cfunc = sleep_f,
+ .flags = CMD_NOFILE_OK,
+ .oneline = "waits for the given value in milliseconds",
+};
+
static void help_oneline(const char *cmd, const cmdinfo_t *ct)
{
if (cmd) {
@@ -2151,4 +2207,5 @@ static void __attribute((constructor)) init_qemuio_commands(void)
qemuio_add_command(&resume_cmd);
qemuio_add_command(&wait_break_cmd);
qemuio_add_command(&abort_cmd);
+ qemuio_add_command(&sleep_cmd);
}
diff --git a/qemu-io.c b/qemu-io.c
index 3b3340ab1b..d6690289b8 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -18,6 +18,7 @@
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
+#include "qemu/readline.h"
#include "block/block_int.h"
#include "trace/control.h"
@@ -32,6 +33,8 @@ extern int qemuio_misalign;
static int ncmdline;
static char **cmdline;
+static ReadLineState *readline_state;
+
static int close_f(BlockDriverState *bs, int argc, char **argv)
{
bdrv_unref(bs);
@@ -56,7 +59,7 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
}
if (growable) {
- if (bdrv_file_open(&qemuio_bs, name, opts, flags, &local_err)) {
+ if (bdrv_file_open(&qemuio_bs, name, NULL, opts, flags, &local_err)) {
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err));
error_free(local_err);
@@ -160,11 +163,13 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
flags |= BDRV_O_RDWR;
}
- if (optind != argc - 1) {
+ if (optind == argc - 1) {
+ return openfile(argv[optind], flags, growable, opts);
+ } else if (optind == argc) {
+ return openfile(NULL, flags, growable, opts);
+ } else {
return qemuio_command_usage(&open_cmd);
}
-
- return openfile(argv[optind], flags, growable, opts);
}
static int quit_f(BlockDriverState *bs, int argc, char **argv)
@@ -203,14 +208,6 @@ static void usage(const char *name)
name);
}
-
-#if defined(ENABLE_READLINE)
-# include <readline/history.h>
-# include <readline/readline.h>
-#elif defined(ENABLE_EDITLINE)
-# include <histedit.h>
-#endif
-
static char *get_prompt(void)
{
static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
@@ -222,52 +219,53 @@ static char *get_prompt(void)
return prompt;
}
-#if defined(ENABLE_READLINE)
-static char *fetchline(void)
+static void readline_printf_func(void *opaque, const char *fmt, ...)
{
- char *line = readline(get_prompt());
- if (line && *line) {
- add_history(line);
- }
- return line;
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
}
-#elif defined(ENABLE_EDITLINE)
-static char *el_get_prompt(EditLine *e)
+
+static void readline_flush_func(void *opaque)
{
- return get_prompt();
+ fflush(stdout);
}
-static char *fetchline(void)
+static void readline_func(void *opaque, const char *str, void *readline_opaque)
{
- static EditLine *el;
- static History *hist;
- HistEvent hevent;
- char *line;
- int count;
-
- if (!el) {
- hist = history_init();
- history(hist, &hevent, H_SETSIZE, 100);
- el = el_init(progname, stdin, stdout, stderr);
- el_source(el, NULL);
- el_set(el, EL_SIGNAL, 1);
- el_set(el, EL_PROMPT, el_get_prompt);
- el_set(el, EL_HIST, history, (const char *)hist);
- }
- line = strdup(el_gets(el, &count));
- if (line) {
- if (count > 0) {
- line[count-1] = '\0';
- }
- if (*line) {
- history(hist, &hevent, H_ENTER, line);
+ char **line = readline_opaque;
+ *line = g_strdup(str);
+}
+
+static void completion_match(const char *cmd, void *opaque)
+{
+ readline_add_completion(readline_state, cmd);
+}
+
+static void readline_completion_func(void *opaque, const char *str)
+{
+ readline_set_completion_index(readline_state, strlen(str));
+ qemuio_complete_command(str, completion_match, NULL);
+}
+
+static char *fetchline_readline(void)
+{
+ char *line = NULL;
+
+ readline_start(readline_state, get_prompt(), 0, readline_func, &line);
+ while (!line) {
+ int ch = getchar();
+ if (ch == EOF) {
+ break;
}
+ readline_handle_byte(readline_state, ch);
}
return line;
}
-#else
-# define MAXREADLINESZ 1024
-static char *fetchline(void)
+
+#define MAXREADLINESZ 1024
+static char *fetchline_fgets(void)
{
char *p, *line = g_malloc(MAXREADLINESZ);
@@ -283,7 +281,15 @@ static char *fetchline(void)
return line;
}
-#endif
+
+static char *fetchline(void)
+{
+ if (readline_state) {
+ return fetchline_readline();
+ } else {
+ return fetchline_fgets();
+ }
+}
static void prep_fetchline(void *opaque)
{
@@ -339,6 +345,11 @@ static void add_user_command(char *optarg)
cmdline[ncmdline-1] = optarg;
}
+static void reenable_tty_echo(void)
+{
+ qemu_set_tty_echo(STDIN_FILENO, true);
+}
+
int main(int argc, char **argv)
{
int readonly = 0;
@@ -435,6 +446,15 @@ int main(int argc, char **argv)
qemuio_add_command(&open_cmd);
qemuio_add_command(&close_cmd);
+ if (isatty(STDIN_FILENO)) {
+ readline_state = readline_init(readline_printf_func,
+ readline_flush_func,
+ NULL,
+ readline_completion_func);
+ qemu_set_tty_echo(STDIN_FILENO, false);
+ atexit(reenable_tty_echo);
+ }
+
/* open the device */
if (!readonly) {
flags |= BDRV_O_RDWR;
@@ -453,5 +473,6 @@ int main(int argc, char **argv)
if (qemuio_bs) {
bdrv_unref(qemuio_bs);
}
+ g_free(readline_state);
return 0;
}
diff --git a/qemu-options.hx b/qemu-options.hx
index af34483021..56e5fdf1e0 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2096,7 +2096,7 @@ ETEXI
DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi,
"-iscsi [user=user][,password=password]\n"
" [,header-digest=CRC32C|CR32C-NONE|NONE-CRC32C|NONE\n"
- " [,initiator-name=iqn]\n"
+ " [,initiator-name=initiator-iqn][,id=target-iqn]\n"
" iSCSI session parameters\n", QEMU_ARCH_ALL)
STEXI
@@ -2419,6 +2419,8 @@ vc:80Cx24C
No device is allocated.
@item null
void device
+@item chardev:@var{id}
+Use a named character device defined with the @code{-chardev} option.
@item /dev/XXX
[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port
parameters are set according to the emulated ones.
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
index cf07869599..caa926ebf2 100644
--- a/qemu-seccomp.c
+++ b/qemu-seccomp.c
@@ -220,7 +220,12 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = {
{ SCMP_SYS(io_cancel), 241 },
{ SCMP_SYS(io_setup), 241 },
{ SCMP_SYS(io_destroy), 241 },
- { SCMP_SYS(arch_prctl), 240 }
+ { SCMP_SYS(arch_prctl), 240 },
+ { SCMP_SYS(mkdir), 240 },
+ { SCMP_SYS(fchmod), 240 },
+ { SCMP_SYS(shmget), 240 },
+ { SCMP_SYS(shmat), 240 },
+ { SCMP_SYS(shmdt), 240 }
};
int seccomp_start(void)
@@ -231,6 +236,7 @@ int seccomp_start(void)
ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL) {
+ rc = -1;
goto seccomp_return;
}
diff --git a/qmp-commands.hx b/qmp-commands.hx
index fba15cdc3b..cce6b81da4 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -879,8 +879,59 @@ Example:
EQMP
{
+ .name = "object-add",
+ .args_type = "qom-type:s,id:s,props:q?",
+ .mhandler.cmd_new = qmp_object_add,
+ },
+
+SQMP
+object-add
+----------
+
+Create QOM object.
+
+Arguments:
+
+- "qom-type": the object's QOM type, i.e. the class name (json-string)
+- "id": the object's ID, must be unique (json-string)
+- "props": a dictionary of object property values (optional, json-dict)
+
+Example:
+
+-> { "execute": "object-add", "arguments": { "qom-type": "rng-random", "id": "rng1",
+ "props": { "filename": "/dev/hwrng" } } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "object-del",
+ .args_type = "id:s",
+ .mhandler.cmd_new = qmp_marshal_input_object_del,
+ },
+
+SQMP
+object-del
+----------
+
+Remove QOM object.
+
+Arguments:
+
+- "id": the object's ID (json-string)
+
+Example:
+
+-> { "execute": "object-del", "arguments": { "id": "rng1" } }
+<- { "return": {} }
+
+
+EQMP
+
+
+ {
.name = "block_resize",
- .args_type = "device:B,size:o",
+ .args_type = "device:s?,node-name:s?,size:o",
.mhandler.cmd_new = qmp_marshal_input_block_resize,
},
@@ -893,6 +944,7 @@ Resize a block image while a guest is running.
Arguments:
- "device": the device's ID, must be unique (json-string)
+- "node-name": the node name in the block driver state graph (json-string)
- "size": new size
Example:
@@ -914,6 +966,45 @@ EQMP
.mhandler.cmd_new = qmp_marshal_input_block_commit,
},
+SQMP
+block-commit
+------------
+
+Live commit of data from overlay image nodes into backing nodes - i.e., writes
+data between 'top' and 'base' into 'base'.
+
+Arguments:
+
+- "device": The device's ID, must be unique (json-string)
+- "base": The file name of the backing image to write data into.
+ If not specified, this is the deepest backing image
+ (json-string, optional)
+- "top": The file name of the backing image within the image chain,
+ which contains the topmost data to be committed down.
+
+ If top == base, that is an error.
+ If top == active, the job will not be completed by itself,
+ user needs to complete the job with the block-job-complete
+ command after getting the ready event. (Since 2.0)
+
+ If the base image is smaller than top, then the base image
+ will be resized to be the same size as top. If top is
+ smaller than the base image, the base will not be
+ truncated. If you want the base image size to match the
+ size of the smaller top, you can safely truncate it
+ yourself once the commit operation successfully completes.
+ (json-string)
+- "speed": the maximum speed, in bytes per second (json-int, optional)
+
+
+Example:
+
+-> { "execute": "block-commit", "arguments": { "device": "virtio0",
+ "top": "/tmp/snap1.qcow2" } }
+<- { "return": {} }
+
+EQMP
+
{
.name = "drive-backup",
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
@@ -1037,7 +1128,9 @@ actions array:
- "data": a dictionary. The contents depend on the value
of "type". When "type" is "blockdev-snapshot-sync":
- "device": device name to snapshot (json-string)
+ - "node-name": graph node name to snapshot (json-string)
- "snapshot-file": name of new image file (json-string)
+ - "snapshot-node-name": graph node name of the new snapshot (json-string)
- "format": format of new image (json-string, optional)
- "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths")
@@ -1052,6 +1145,11 @@ Example:
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd0",
"snapshot-file": "/some/place/my-image",
"format": "qcow2" } },
+ { 'type': 'blockdev-snapshot-sync', 'data' : { "node-name": "myfile",
+ "snapshot-file": "/some/place/my-image2",
+ "snapshot-node-name": "node3432",
+ "mode": "existing",
+ "format": "qcow2" } },
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
"snapshot-file": "/some/place/my-image2",
"mode": "existing",
@@ -1065,7 +1163,7 @@ EQMP
{
.name = "blockdev-snapshot-sync",
- .args_type = "device:B,snapshot-file:s,format:s?,mode:s?",
+ .args_type = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?",
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync,
},
@@ -1082,7 +1180,9 @@ snapshot image, default is qcow2.
Arguments:
- "device": device name to snapshot (json-string)
+- "node-name": graph node name to snapshot (json-string)
- "snapshot-file": name of new image file (json-string)
+- "snapshot-node-name": graph node name of the new snapshot (json-string)
- "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths")
- "format": format of new image (json-string, optional)
@@ -1452,7 +1552,7 @@ EQMP
{
.name = "block_passwd",
- .args_type = "device:B,password:s",
+ .args_type = "device:s?,node-name:s?,password:s",
.mhandler.cmd_new = qmp_marshal_input_block_passwd,
},
@@ -1465,6 +1565,7 @@ Set the password of encrypted block devices.
Arguments:
- "device": device name (json-string)
+- "node-name": name in the block driver state graph (json-string)
- "password": password (json-string)
Example:
@@ -3295,3 +3396,64 @@ Example (2):
<- { "return": {} }
EQMP
+
+ {
+ .name = "query-named-block-nodes",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_named_block_nodes,
+ },
+
+SQMP
+@query-named-block-nodes
+------------------------
+
+Return a list of BlockDeviceInfo for all the named block driver nodes
+
+Example:
+
+-> { "execute": "query-named-block-nodes" }
+<- { "return": [ { "ro":false,
+ "drv":"qcow2",
+ "encrypted":false,
+ "file":"disks/test.qcow2",
+ "node-name": "my-node",
+ "backing_file_depth":1,
+ "bps":1000000,
+ "bps_rd":0,
+ "bps_wr":0,
+ "iops":1000000,
+ "iops_rd":0,
+ "iops_wr":0,
+ "bps_max": 8000000,
+ "bps_rd_max": 0,
+ "bps_wr_max": 0,
+ "iops_max": 0,
+ "iops_rd_max": 0,
+ "iops_wr_max": 0,
+ "iops_size": 0,
+ "image":{
+ "filename":"disks/test.qcow2",
+ "format":"qcow2",
+ "virtual-size":2048000,
+ "backing_file":"base.qcow2",
+ "full-backing-filename":"disks/base.qcow2",
+ "backing-filename-format:"qcow2",
+ "snapshots":[
+ {
+ "id": "1",
+ "name": "snapshot1",
+ "vm-state-size": 0,
+ "date-sec": 10000200,
+ "date-nsec": 12,
+ "vm-clock-sec": 206,
+ "vm-clock-nsec": 30
+ }
+ ],
+ "backing-image":{
+ "filename":"disks/base.qcow2",
+ "format":"qcow2",
+ "virtual-size":2048000
+ }
+ } } ] }
+
+EQMP
diff --git a/qmp.c b/qmp.c
index 4c149b33a4..0f46171aad 100644
--- a/qmp.c
+++ b/qmp.c
@@ -24,6 +24,8 @@
#include "hw/qdev.h"
#include "sysemu/blockdev.h"
#include "qom/qom-qobject.h"
+#include "qapi/qmp/qobject.h"
+#include "qapi/qmp-input-visitor.h"
#include "hw/boards.h"
NameInfo *qmp_query_name(Error **errp)
@@ -400,7 +402,7 @@ void qmp_change(const char *device, const char *target,
if (strcmp(device, "vnc") == 0) {
qmp_change_vnc(target, has_arg, arg, err);
} else {
- qmp_change_blockdev(device, target, has_arg, arg, err);
+ qmp_change_blockdev(device, target, arg, err);
}
}
@@ -529,3 +531,77 @@ void qmp_add_client(const char *protocol, const char *fdname,
error_setg(errp, "protocol '%s' is invalid", protocol);
close(fd);
}
+
+void object_add(const char *type, const char *id, const QDict *qdict,
+ Visitor *v, Error **errp)
+{
+ Object *obj;
+ const QDictEntry *e;
+ Error *local_err = NULL;
+
+ if (!object_class_by_name(type)) {
+ error_setg(errp, "invalid class name");
+ return;
+ }
+
+ obj = object_new(type);
+ if (qdict) {
+ for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
+ object_property_set(obj, v, e->key, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ object_unref(obj);
+ return;
+ }
+ }
+ }
+
+ object_property_add_child(container_get(object_get_root(), "/objects"),
+ id, obj, errp);
+ object_unref(obj);
+}
+
+int qmp_object_add(Monitor *mon, const QDict *qdict, QObject **ret)
+{
+ const char *type = qdict_get_str(qdict, "qom-type");
+ const char *id = qdict_get_str(qdict, "id");
+ QObject *props = qdict_get(qdict, "props");
+ const QDict *pdict = NULL;
+ Error *local_err = NULL;
+ QmpInputVisitor *qiv;
+
+ if (props) {
+ pdict = qobject_to_qdict(props);
+ if (!pdict) {
+ error_set(&local_err, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
+ goto out;
+ }
+ }
+
+ qiv = qmp_input_visitor_new(props);
+ object_add(type, id, pdict, qmp_input_get_visitor(qiv), &local_err);
+ qmp_input_visitor_cleanup(qiv);
+
+out:
+ if (local_err) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
+ return 0;
+}
+
+void qmp_object_del(const char *id, Error **errp)
+{
+ Object *container;
+ Object *obj;
+
+ container = container_get(object_get_root(), "/objects");
+ obj = object_resolve_path_component(container, id);
+ if (!obj) {
+ error_setg(errp, "object id not found");
+ return;
+ }
+ object_unparent(obj);
+}
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 17e14f08b1..a3924f24bd 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -477,7 +477,43 @@ static void qdict_destroy_obj(QObject *obj)
g_free(qdict);
}
-static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
+static void qdict_flatten_qdict(QDict *qdict, QDict *target,
+ const char *prefix);
+
+static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
+{
+ QObject *value;
+ const QListEntry *entry;
+ char *new_key;
+ int i;
+
+ /* This function is never called with prefix == NULL, i.e., it is always
+ * called from within qdict_flatten_q(list|dict)(). Therefore, it does not
+ * need to remove list entries during the iteration (the whole list will be
+ * deleted eventually anyway from qdict_flatten_qdict()). */
+ assert(prefix);
+
+ entry = qlist_first(qlist);
+
+ for (i = 0; entry; entry = qlist_next(entry), i++) {
+ value = qlist_entry_obj(entry);
+ new_key = g_strdup_printf("%s.%i", prefix, i);
+
+ if (qobject_type(value) == QTYPE_QDICT) {
+ qdict_flatten_qdict(qobject_to_qdict(value), target, new_key);
+ } else if (qobject_type(value) == QTYPE_QLIST) {
+ qdict_flatten_qlist(qobject_to_qlist(value), target, new_key);
+ } else {
+ /* All other types are moved to the target unchanged. */
+ qobject_incref(value);
+ qdict_put_obj(target, new_key, value);
+ }
+
+ g_free(new_key);
+ }
+}
+
+static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
{
QObject *value;
const QDictEntry *entry, *next;
@@ -500,8 +536,12 @@ static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
if (qobject_type(value) == QTYPE_QDICT) {
/* Entries of QDicts are processed recursively, the QDict object
* itself disappears. */
- qdict_do_flatten(qobject_to_qdict(value), target,
- new_key ? new_key : entry->key);
+ qdict_flatten_qdict(qobject_to_qdict(value), target,
+ new_key ? new_key : entry->key);
+ delete = true;
+ } else if (qobject_type(value) == QTYPE_QLIST) {
+ qdict_flatten_qlist(qobject_to_qlist(value), target,
+ new_key ? new_key : entry->key);
delete = true;
} else if (prefix) {
/* All other objects are moved to the target unchanged. */
@@ -526,12 +566,14 @@ static void qdict_do_flatten(QDict *qdict, QDict *target, const char *prefix)
/**
* qdict_flatten(): For each nested QDict with key x, all fields with key y
- * are moved to this QDict and their key is renamed to "x.y". This operation
- * is applied recursively for nested QDicts.
+ * are moved to this QDict and their key is renamed to "x.y". For each nested
+ * QList with key x, the field at index y is moved to this QDict with the key
+ * "x.y" (i.e., the reverse of what qdict_array_split() does).
+ * This operation is applied recursively for nested QDicts and QLists.
*/
void qdict_flatten(QDict *qdict)
{
- qdict_do_flatten(qdict, qdict, NULL);
+ qdict_flatten_qdict(qdict, qdict, NULL);
}
/* extract all the src QDict entries starting by start into dst */
@@ -554,3 +596,40 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
entry = next;
}
}
+
+/**
+ * qdict_array_split(): This function moves array-like elements of a QDict into
+ * a new QList of QDicts. Every entry in the original QDict with a key prefixed
+ * "%u.", where %u designates an unsigned integer starting at 0 and
+ * incrementally counting up, will be moved to a new QDict at index %u in the
+ * output QList with the key prefix removed. The function terminates when there
+ * is no entry in the QDict with a prefix directly (incrementally) following the
+ * last one.
+ * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "3.y": 1, "o.o": 7}
+ * (or {"1.x": 0, "3.y": 1, "0.a": 42, "o.o": 7, "0.b": 23})
+ * => [{"a": 42, "b": 23}, {"x": 0}]
+ * and {"3.y": 1, "o.o": 7} (remainder of the old QDict)
+ */
+void qdict_array_split(QDict *src, QList **dst)
+{
+ unsigned i;
+
+ *dst = qlist_new();
+
+ for (i = 0; i < UINT_MAX; i++) {
+ QDict *subqdict;
+ char prefix[32];
+ size_t snprintf_ret;
+
+ snprintf_ret = snprintf(prefix, 32, "%u.", i);
+ assert(snprintf_ret < 32);
+
+ qdict_extract_subqdict(src, &subqdict, prefix);
+ if (!qdict_size(subqdict)) {
+ QDECREF(subqdict);
+ break;
+ }
+
+ qlist_append_obj(*dst, QOBJECT(subqdict));
+ }
+}
diff --git a/qobject/qerror.c b/qobject/qerror.c
index fc8331aa67..e3608e2402 100644
--- a/qobject/qerror.c
+++ b/qobject/qerror.c
@@ -121,14 +121,6 @@ void qerror_report_err(Error *err)
}
}
-void assert_no_error(Error *err)
-{
- if (err) {
- qerror_report_err(err);
- abort();
- }
-}
-
/**
* qobject_to_qerror(): Convert a QObject into a QError
*/
diff --git a/qom/cpu.c b/qom/cpu.c
index 818fb26dd4..9d62479546 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -254,7 +254,11 @@ static void cpu_class_init(ObjectClass *klass, void *data)
k->gdb_read_register = cpu_common_gdb_read_register;
k->gdb_write_register = cpu_common_gdb_write_register;
dc->realize = cpu_common_realizefn;
- dc->no_user = 1;
+ /*
+ * Reason: CPUs still need special care by board code: wiring up
+ * IRQs, adding reset handlers, halting non-first CPUs, ...
+ */
+ dc->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo cpu_type_info = {
diff --git a/qom/object.c b/qom/object.c
index fc19cf676a..62e7e415d9 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -78,8 +78,11 @@ static GHashTable *type_table_get(void)
return type_table;
}
+static bool enumerating_types;
+
static void type_table_add(TypeImpl *ti)
{
+ assert(!enumerating_types);
g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
}
@@ -88,7 +91,7 @@ static TypeImpl *type_table_lookup(const char *name)
return g_hash_table_lookup(type_table_get(), name);
}
-static TypeImpl *type_register_internal(const TypeInfo *info)
+static TypeImpl *type_new(const TypeInfo *info)
{
TypeImpl *ti = g_malloc0(sizeof(*ti));
int i;
@@ -122,8 +125,15 @@ static TypeImpl *type_register_internal(const TypeInfo *info)
}
ti->num_interfaces = i;
- type_table_add(ti);
+ return ti;
+}
+static TypeImpl *type_register_internal(const TypeInfo *info)
+{
+ TypeImpl *ti;
+ ti = type_new(info);
+
+ type_table_add(ti);
return ti;
}
@@ -206,22 +216,25 @@ static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type)
static void type_initialize(TypeImpl *ti);
-static void type_initialize_interface(TypeImpl *ti, const char *parent)
+static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type,
+ TypeImpl *parent_type)
{
InterfaceClass *new_iface;
TypeInfo info = { };
TypeImpl *iface_impl;
- info.parent = parent;
- info.name = g_strdup_printf("%s::%s", ti->name, info.parent);
+ info.parent = parent_type->name;
+ info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name);
info.abstract = true;
- iface_impl = type_register(&info);
+ iface_impl = type_new(&info);
+ iface_impl->parent_type = parent_type;
type_initialize(iface_impl);
g_free((char *)info.name);
new_iface = (InterfaceClass *)iface_impl->class;
new_iface->concrete_class = ti->class;
+ new_iface->interface_type = interface_type;
ti->class->interfaces = g_slist_append(ti->class->interfaces,
iface_impl->class);
@@ -251,8 +264,10 @@ static void type_initialize(TypeImpl *ti)
ti->class->interfaces = NULL;
for (e = parent->class->interfaces; e; e = e->next) {
- ObjectClass *iface = e->data;
- type_initialize_interface(ti, object_class_get_name(iface));
+ InterfaceClass *iface = e->data;
+ ObjectClass *klass = OBJECT_CLASS(iface);
+
+ type_initialize_interface(ti, iface->interface_type, klass->type);
}
for (i = 0; i < ti->num_interfaces; i++) {
@@ -269,7 +284,7 @@ static void type_initialize(TypeImpl *ti)
continue;
}
- type_initialize_interface(ti, ti->interfaces[i].typename);
+ type_initialize_interface(ti, t, t);
}
}
@@ -285,8 +300,6 @@ static void type_initialize(TypeImpl *ti)
if (ti->class_init) {
ti->class_init(ti->class, ti->class_data);
}
-
-
}
static void object_init_with_type(Object *obj, TypeImpl *ti)
@@ -458,7 +471,7 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename,
Object *inst;
for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) {
- if (obj->class->cast_cache[i] == typename) {
+ if (obj->class->object_cast_cache[i] == typename) {
goto out;
}
}
@@ -475,9 +488,10 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename,
if (obj && obj == inst) {
for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) {
- obj->class->cast_cache[i - 1] = obj->class->cast_cache[i];
+ obj->class->object_cast_cache[i - 1] =
+ obj->class->object_cast_cache[i];
}
- obj->class->cast_cache[i - 1] = typename;
+ obj->class->object_cast_cache[i - 1] = typename;
}
out:
@@ -547,7 +561,7 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class,
int i;
for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) {
- if (class->cast_cache[i] == typename) {
+ if (class->class_cast_cache[i] == typename) {
ret = class;
goto out;
}
@@ -568,9 +582,9 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class,
#ifdef CONFIG_QOM_CAST_DEBUG
if (class && ret == class) {
for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) {
- class->cast_cache[i - 1] = class->cast_cache[i];
+ class->class_cast_cache[i - 1] = class->class_cast_cache[i];
}
- class->cast_cache[i - 1] = typename;
+ class->class_cast_cache[i - 1] = typename;
}
out:
#endif
@@ -659,7 +673,9 @@ void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque),
{
OCFData data = { fn, implements_type, include_abstract, opaque };
+ enumerating_types = true;
g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data);
+ enumerating_types = false;
}
int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque),
@@ -988,17 +1004,22 @@ static void object_finalize_child_property(Object *obj, const char *name,
void object_property_add_child(Object *obj, const char *name,
Object *child, Error **errp)
{
+ Error *local_err = NULL;
gchar *type;
type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child)));
- object_property_add(obj, name, type, object_get_child_property,
- NULL, object_finalize_child_property, child, errp);
-
+ object_property_add(obj, name, type, object_get_child_property, NULL,
+ object_finalize_child_property, child, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
object_ref(child);
g_assert(child->parent == NULL);
child->parent = obj;
+out:
g_free(type);
}
diff --git a/qtest.c b/qtest.c
index 584c70762a..dcf1301229 100644
--- a/qtest.c
+++ b/qtest.c
@@ -22,8 +22,6 @@
#define MAX_IRQ 256
-const char *qtest_chrdev;
-const char *qtest_log;
bool qtest_allowed;
static DeviceState *irq_intercept_dev;
@@ -406,7 +404,7 @@ static void qtest_process_command(CharDriverState *chr, gchar **words)
qtest_send_prefix(chr);
qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "clock_step") == 0) {
+ } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) {
int64_t ns;
if (words[1]) {
@@ -417,7 +415,7 @@ static void qtest_process_command(CharDriverState *chr, gchar **words)
qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
qtest_send_prefix(chr);
qtest_send(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
- } else if (strcmp(words[0], "clock_set") == 0) {
+ } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) {
int64_t ns;
g_assert(words[1]);
@@ -502,13 +500,17 @@ static void qtest_event(void *opaque, int event)
}
}
-int qtest_init(void)
+int qtest_init_accel(void)
{
- CharDriverState *chr;
+ configure_icount("0");
- g_assert(qtest_chrdev != NULL);
+ return 0;
+}
+
+void qtest_init(const char *qtest_chrdev, const char *qtest_log)
+{
+ CharDriverState *chr;
- configure_icount("0");
chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
@@ -525,6 +527,4 @@ int qtest_init(void)
}
qtest_chr = chr;
-
- return 0;
}
diff --git a/savevm.c b/savevm.c
index 3f912ddcf9..a7dbe18a67 100644
--- a/savevm.c
+++ b/savevm.c
@@ -38,7 +38,6 @@
#include "exec/memory.h"
#include "qmp-commands.h"
#include "trace.h"
-#include "qemu/bitops.h"
#include "qemu/iov.h"
#include "block/snapshot.h"
#include "block/qapi.h"
@@ -53,7 +52,7 @@
#define ARP_OP_REQUEST_REV 0x3
static int announce_self_create(uint8_t *buf,
- uint8_t *mac_addr)
+ uint8_t *mac_addr)
{
/* Ethernet header. */
memset(buf, 0xff, 6); /* destination MAC addr */
@@ -100,411 +99,21 @@ static void qemu_announce_self_once(void *opaque)
timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) +
50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100);
} else {
- timer_del(timer);
- timer_free(timer);
+ timer_del(timer);
+ timer_free(timer);
}
}
void qemu_announce_self(void)
{
- static QEMUTimer *timer;
- timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer);
- qemu_announce_self_once(&timer);
+ static QEMUTimer *timer;
+ timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer);
+ qemu_announce_self_once(&timer);
}
/***********************************************************/
/* savevm/loadvm support */
-#define IO_BUF_SIZE 32768
-#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
-
-struct QEMUFile {
- const QEMUFileOps *ops;
- void *opaque;
-
- int64_t bytes_xfer;
- int64_t xfer_limit;
-
- int64_t pos; /* start of buffer when writing, end of buffer
- when reading */
- int buf_index;
- int buf_size; /* 0 when writing */
- uint8_t buf[IO_BUF_SIZE];
-
- struct iovec iov[MAX_IOV_SIZE];
- unsigned int iovcnt;
-
- int last_error;
-};
-
-typedef struct QEMUFileStdio
-{
- FILE *stdio_file;
- QEMUFile *file;
-} QEMUFileStdio;
-
-typedef struct QEMUFileSocket
-{
- int fd;
- QEMUFile *file;
-} QEMUFileSocket;
-
-static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
- int64_t pos)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len;
- ssize_t size = iov_size(iov, iovcnt);
-
- len = iov_send(s->fd, iov, iovcnt, 0, size);
- if (len < size) {
- len = -socket_error();
- }
- return len;
-}
-
-static int socket_get_fd(void *opaque)
-{
- QEMUFileSocket *s = opaque;
-
- return s->fd;
-}
-
-static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len;
-
- for (;;) {
- len = qemu_recv(s->fd, buf, size, 0);
- if (len != -1) {
- break;
- }
- if (socket_error() == EAGAIN) {
- yield_until_fd_readable(s->fd);
- } else if (socket_error() != EINTR) {
- break;
- }
- }
-
- if (len == -1) {
- len = -socket_error();
- }
- return len;
-}
-
-static int socket_close(void *opaque)
-{
- QEMUFileSocket *s = opaque;
- closesocket(s->fd);
- g_free(s);
- return 0;
-}
-
-static int stdio_get_fd(void *opaque)
-{
- QEMUFileStdio *s = opaque;
-
- return fileno(s->stdio_file);
-}
-
-static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- FILE *fp = s->stdio_file;
- int bytes;
-
- for (;;) {
- clearerr(fp);
- bytes = fread(buf, 1, size, fp);
- if (bytes != 0 || !ferror(fp)) {
- break;
- }
- if (errno == EAGAIN) {
- yield_until_fd_readable(fileno(fp));
- } else if (errno != EINTR) {
- break;
- }
- }
- return bytes;
-}
-
-static int stdio_pclose(void *opaque)
-{
- QEMUFileStdio *s = opaque;
- int ret;
- ret = pclose(s->stdio_file);
- if (ret == -1) {
- ret = -errno;
- } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
- /* close succeeded, but non-zero exit code: */
- ret = -EIO; /* fake errno value */
- }
- g_free(s);
- return ret;
-}
-
-static int stdio_fclose(void *opaque)
-{
- QEMUFileStdio *s = opaque;
- int ret = 0;
-
- if (s->file->ops->put_buffer || s->file->ops->writev_buffer) {
- int fd = fileno(s->stdio_file);
- struct stat st;
-
- ret = fstat(fd, &st);
- if (ret == 0 && S_ISREG(st.st_mode)) {
- /*
- * If the file handle is a regular file make sure the
- * data is flushed to disk before signaling success.
- */
- ret = fsync(fd);
- if (ret != 0) {
- ret = -errno;
- return ret;
- }
- }
- }
- if (fclose(s->stdio_file) == EOF) {
- ret = -errno;
- }
- g_free(s);
- return ret;
-}
-
-static const QEMUFileOps stdio_pipe_read_ops = {
- .get_fd = stdio_get_fd,
- .get_buffer = stdio_get_buffer,
- .close = stdio_pclose
-};
-
-static const QEMUFileOps stdio_pipe_write_ops = {
- .get_fd = stdio_get_fd,
- .put_buffer = stdio_put_buffer,
- .close = stdio_pclose
-};
-
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
- FILE *stdio_file;
- QEMUFileStdio *s;
-
- if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
- fprintf(stderr, "qemu_popen: Argument validity check failed\n");
- return NULL;
- }
-
- stdio_file = popen(command, mode);
- if (stdio_file == NULL) {
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileStdio));
-
- s->stdio_file = stdio_file;
-
- if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
- } else {
- s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
- }
- return s->file;
-}
-
-static const QEMUFileOps stdio_file_read_ops = {
- .get_fd = stdio_get_fd,
- .get_buffer = stdio_get_buffer,
- .close = stdio_fclose
-};
-
-static const QEMUFileOps stdio_file_write_ops = {
- .get_fd = stdio_get_fd,
- .put_buffer = stdio_put_buffer,
- .close = stdio_fclose
-};
-
-static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
- int64_t pos)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len, offset;
- ssize_t size = iov_size(iov, iovcnt);
- ssize_t total = 0;
-
- assert(iovcnt > 0);
- offset = 0;
- while (size > 0) {
- /* Find the next start position; skip all full-sized vector elements */
- while (offset >= iov[0].iov_len) {
- offset -= iov[0].iov_len;
- iov++, iovcnt--;
- }
-
- /* skip `offset' bytes from the (now) first element, undo it on exit */
- assert(iovcnt > 0);
- iov[0].iov_base += offset;
- iov[0].iov_len -= offset;
-
- do {
- len = writev(s->fd, iov, iovcnt);
- } while (len == -1 && errno == EINTR);
- if (len == -1) {
- return -errno;
- }
-
- /* Undo the changes above */
- iov[0].iov_base -= offset;
- iov[0].iov_len += offset;
-
- /* Prepare for the next iteration */
- offset += len;
- total += len;
- size -= len;
- }
-
- return total;
-}
-
-static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len;
-
- for (;;) {
- len = read(s->fd, buf, size);
- if (len != -1) {
- break;
- }
- if (errno == EAGAIN) {
- yield_until_fd_readable(s->fd);
- } else if (errno != EINTR) {
- break;
- }
- }
-
- if (len == -1) {
- len = -errno;
- }
- return len;
-}
-
-static int unix_close(void *opaque)
-{
- QEMUFileSocket *s = opaque;
- close(s->fd);
- g_free(s);
- return 0;
-}
-
-static const QEMUFileOps unix_read_ops = {
- .get_fd = socket_get_fd,
- .get_buffer = unix_get_buffer,
- .close = unix_close
-};
-
-static const QEMUFileOps unix_write_ops = {
- .get_fd = socket_get_fd,
- .writev_buffer = unix_writev_buffer,
- .close = unix_close
-};
-
-QEMUFile *qemu_fdopen(int fd, const char *mode)
-{
- QEMUFileSocket *s;
-
- if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileSocket));
- s->fd = fd;
-
- if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, &unix_read_ops);
- } else {
- s->file = qemu_fopen_ops(s, &unix_write_ops);
- }
- return s->file;
-}
-
-static const QEMUFileOps socket_read_ops = {
- .get_fd = socket_get_fd,
- .get_buffer = socket_get_buffer,
- .close = socket_close
-};
-
-static const QEMUFileOps socket_write_ops = {
- .get_fd = socket_get_fd,
- .writev_buffer = socket_writev_buffer,
- .close = socket_close
-};
-
-bool qemu_file_mode_is_not_valid(const char *mode)
-{
- if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
- return true;
- }
-
- return false;
-}
-
-QEMUFile *qemu_fopen_socket(int fd, const char *mode)
-{
- QEMUFileSocket *s;
-
- if (qemu_file_mode_is_not_valid(mode)) {
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileSocket));
- s->fd = fd;
- if (mode[0] == 'w') {
- qemu_set_block(s->fd);
- s->file = qemu_fopen_ops(s, &socket_write_ops);
- } else {
- s->file = qemu_fopen_ops(s, &socket_read_ops);
- }
- return s->file;
-}
-
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
-{
- QEMUFileStdio *s;
-
- if (qemu_file_mode_is_not_valid(mode)) {
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileStdio));
-
- s->stdio_file = fopen(filename, mode);
- if (!s->stdio_file)
- goto fail;
-
- if(mode[0] == 'w') {
- s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
- } else {
- s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
- }
- return s->file;
-fail:
- g_free(s);
- return NULL;
-}
-
static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
int64_t pos)
{
@@ -550,441 +159,16 @@ static const QEMUFileOps bdrv_write_ops = {
static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
{
- if (is_writable)
+ if (is_writable) {
return qemu_fopen_ops(bs, &bdrv_write_ops);
- return qemu_fopen_ops(bs, &bdrv_read_ops);
-}
-
-QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
-{
- QEMUFile *f;
-
- f = g_malloc0(sizeof(QEMUFile));
-
- f->opaque = opaque;
- f->ops = ops;
- return f;
-}
-
-/*
- * Get last error for stream f
- *
- * Return negative error value if there has been an error on previous
- * operations, return 0 if no error happened.
- *
- */
-int qemu_file_get_error(QEMUFile *f)
-{
- return f->last_error;
-}
-
-static void qemu_file_set_error(QEMUFile *f, int ret)
-{
- if (f->last_error == 0) {
- f->last_error = ret;
}
+ return qemu_fopen_ops(bs, &bdrv_read_ops);
}
-static inline bool qemu_file_is_writable(QEMUFile *f)
-{
- return f->ops->writev_buffer || f->ops->put_buffer;
-}
-/**
- * Flushes QEMUFile buffer
- *
- * If there is writev_buffer QEMUFileOps it uses it otherwise uses
- * put_buffer ops.
+/* QEMUFile timer support.
+ * Not in qemu-file.c to not add qemu-timer.c as dependency to qemu-file.c
*/
-void qemu_fflush(QEMUFile *f)
-{
- ssize_t ret = 0;
-
- if (!qemu_file_is_writable(f)) {
- return;
- }
-
- if (f->ops->writev_buffer) {
- if (f->iovcnt > 0) {
- ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
- }
- } else {
- if (f->buf_index > 0) {
- ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
- }
- }
- if (ret >= 0) {
- f->pos += ret;
- }
- f->buf_index = 0;
- f->iovcnt = 0;
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
-}
-
-void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
-{
- int ret = 0;
-
- if (f->ops->before_ram_iterate) {
- ret = f->ops->before_ram_iterate(f, f->opaque, flags);
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
- }
-}
-
-void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
-{
- int ret = 0;
-
- if (f->ops->after_ram_iterate) {
- ret = f->ops->after_ram_iterate(f, f->opaque, flags);
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
- }
-}
-
-void ram_control_load_hook(QEMUFile *f, uint64_t flags)
-{
- int ret = -EINVAL;
-
- if (f->ops->hook_ram_load) {
- ret = f->ops->hook_ram_load(f, f->opaque, flags);
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
- } else {
- qemu_file_set_error(f, ret);
- }
-}
-
-size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
- ram_addr_t offset, size_t size, int *bytes_sent)
-{
- if (f->ops->save_page) {
- int ret = f->ops->save_page(f, f->opaque, block_offset,
- offset, size, bytes_sent);
-
- if (ret != RAM_SAVE_CONTROL_DELAYED) {
- if (bytes_sent && *bytes_sent > 0) {
- qemu_update_position(f, *bytes_sent);
- } else if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
- }
-
- return ret;
- }
-
- return RAM_SAVE_CONTROL_NOT_SUPP;
-}
-
-static void qemu_fill_buffer(QEMUFile *f)
-{
- int len;
- int pending;
-
- assert(!qemu_file_is_writable(f));
-
- pending = f->buf_size - f->buf_index;
- if (pending > 0) {
- memmove(f->buf, f->buf + f->buf_index, pending);
- }
- f->buf_index = 0;
- f->buf_size = pending;
-
- len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
- IO_BUF_SIZE - pending);
- if (len > 0) {
- f->buf_size += len;
- f->pos += len;
- } else if (len == 0) {
- qemu_file_set_error(f, -EIO);
- } else if (len != -EAGAIN)
- qemu_file_set_error(f, len);
-}
-
-int qemu_get_fd(QEMUFile *f)
-{
- if (f->ops->get_fd) {
- return f->ops->get_fd(f->opaque);
- }
- return -1;
-}
-
-void qemu_update_position(QEMUFile *f, size_t size)
-{
- f->pos += size;
-}
-
-/** Closes the file
- *
- * Returns negative error value if any error happened on previous operations or
- * while closing the file. Returns 0 or positive number on success.
- *
- * The meaning of return value on success depends on the specific backend
- * being used.
- */
-int qemu_fclose(QEMUFile *f)
-{
- int ret;
- qemu_fflush(f);
- ret = qemu_file_get_error(f);
-
- if (f->ops->close) {
- int ret2 = f->ops->close(f->opaque);
- if (ret >= 0) {
- ret = ret2;
- }
- }
- /* If any error was spotted before closing, we should report it
- * instead of the close() return value.
- */
- if (f->last_error) {
- ret = f->last_error;
- }
- g_free(f);
- return ret;
-}
-
-static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
-{
- /* check for adjacent buffer and coalesce them */
- if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
- f->iov[f->iovcnt - 1].iov_len) {
- f->iov[f->iovcnt - 1].iov_len += size;
- } else {
- f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
- f->iov[f->iovcnt++].iov_len = size;
- }
-
- if (f->iovcnt >= MAX_IOV_SIZE) {
- qemu_fflush(f);
- }
-}
-
-void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
-{
- if (!f->ops->writev_buffer) {
- qemu_put_buffer(f, buf, size);
- return;
- }
-
- if (f->last_error) {
- return;
- }
-
- f->bytes_xfer += size;
- add_to_iovec(f, buf, size);
-}
-
-void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
-{
- int l;
-
- if (f->last_error) {
- return;
- }
-
- while (size > 0) {
- l = IO_BUF_SIZE - f->buf_index;
- if (l > size)
- l = size;
- memcpy(f->buf + f->buf_index, buf, l);
- f->bytes_xfer += l;
- if (f->ops->writev_buffer) {
- add_to_iovec(f, f->buf + f->buf_index, l);
- }
- f->buf_index += l;
- if (f->buf_index == IO_BUF_SIZE) {
- qemu_fflush(f);
- }
- if (qemu_file_get_error(f)) {
- break;
- }
- buf += l;
- size -= l;
- }
-}
-
-void qemu_put_byte(QEMUFile *f, int v)
-{
- if (f->last_error) {
- return;
- }
-
- f->buf[f->buf_index] = v;
- f->bytes_xfer++;
- if (f->ops->writev_buffer) {
- add_to_iovec(f, f->buf + f->buf_index, 1);
- }
- f->buf_index++;
- if (f->buf_index == IO_BUF_SIZE) {
- qemu_fflush(f);
- }
-}
-
-static void qemu_file_skip(QEMUFile *f, int size)
-{
- if (f->buf_index + size <= f->buf_size) {
- f->buf_index += size;
- }
-}
-
-static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
-{
- int pending;
- int index;
-
- assert(!qemu_file_is_writable(f));
-
- index = f->buf_index + offset;
- pending = f->buf_size - index;
- if (pending < size) {
- qemu_fill_buffer(f);
- index = f->buf_index + offset;
- pending = f->buf_size - index;
- }
-
- if (pending <= 0) {
- return 0;
- }
- if (size > pending) {
- size = pending;
- }
-
- memcpy(buf, f->buf + index, size);
- return size;
-}
-
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
-{
- int pending = size;
- int done = 0;
-
- while (pending > 0) {
- int res;
-
- res = qemu_peek_buffer(f, buf, pending, 0);
- if (res == 0) {
- return done;
- }
- qemu_file_skip(f, res);
- buf += res;
- pending -= res;
- done += res;
- }
- return done;
-}
-
-static int qemu_peek_byte(QEMUFile *f, int offset)
-{
- int index = f->buf_index + offset;
-
- assert(!qemu_file_is_writable(f));
-
- if (index >= f->buf_size) {
- qemu_fill_buffer(f);
- index = f->buf_index + offset;
- if (index >= f->buf_size) {
- return 0;
- }
- }
- return f->buf[index];
-}
-
-int qemu_get_byte(QEMUFile *f)
-{
- int result;
-
- result = qemu_peek_byte(f, 0);
- qemu_file_skip(f, 1);
- return result;
-}
-
-int64_t qemu_ftell(QEMUFile *f)
-{
- qemu_fflush(f);
- return f->pos;
-}
-
-int qemu_file_rate_limit(QEMUFile *f)
-{
- if (qemu_file_get_error(f)) {
- return 1;
- }
- if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
- return 1;
- }
- return 0;
-}
-
-int64_t qemu_file_get_rate_limit(QEMUFile *f)
-{
- return f->xfer_limit;
-}
-
-void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
-{
- f->xfer_limit = limit;
-}
-
-void qemu_file_reset_rate_limit(QEMUFile *f)
-{
- f->bytes_xfer = 0;
-}
-
-void qemu_put_be16(QEMUFile *f, unsigned int v)
-{
- qemu_put_byte(f, v >> 8);
- qemu_put_byte(f, v);
-}
-
-void qemu_put_be32(QEMUFile *f, unsigned int v)
-{
- qemu_put_byte(f, v >> 24);
- qemu_put_byte(f, v >> 16);
- qemu_put_byte(f, v >> 8);
- qemu_put_byte(f, v);
-}
-
-void qemu_put_be64(QEMUFile *f, uint64_t v)
-{
- qemu_put_be32(f, v >> 32);
- qemu_put_be32(f, v);
-}
-
-unsigned int qemu_get_be16(QEMUFile *f)
-{
- unsigned int v;
- v = qemu_get_byte(f) << 8;
- v |= qemu_get_byte(f);
- return v;
-}
-
-unsigned int qemu_get_be32(QEMUFile *f)
-{
- unsigned int v;
- v = qemu_get_byte(f) << 24;
- v |= qemu_get_byte(f) << 16;
- v |= qemu_get_byte(f) << 8;
- v |= qemu_get_byte(f);
- return v;
-}
-
-uint64_t qemu_get_be64(QEMUFile *f)
-{
- uint64_t v;
- v = (uint64_t)qemu_get_be32(f) << 32;
- v |= qemu_get_be32(f);
- return v;
-}
-
-
-/* timer */
void timer_put(QEMUFile *f, QEMUTimer *ts)
{
@@ -1007,341 +191,9 @@ void timer_get(QEMUFile *f, QEMUTimer *ts)
}
-/* bool */
-
-static int get_bool(QEMUFile *f, void *pv, size_t size)
-{
- bool *v = pv;
- *v = qemu_get_byte(f);
- return 0;
-}
-
-static void put_bool(QEMUFile *f, void *pv, size_t size)
-{
- bool *v = pv;
- qemu_put_byte(f, *v);
-}
-
-const VMStateInfo vmstate_info_bool = {
- .name = "bool",
- .get = get_bool,
- .put = put_bool,
-};
-
-/* 8 bit int */
-
-static int get_int8(QEMUFile *f, void *pv, size_t size)
-{
- int8_t *v = pv;
- qemu_get_s8s(f, v);
- return 0;
-}
-
-static void put_int8(QEMUFile *f, void *pv, size_t size)
-{
- int8_t *v = pv;
- qemu_put_s8s(f, v);
-}
-
-const VMStateInfo vmstate_info_int8 = {
- .name = "int8",
- .get = get_int8,
- .put = put_int8,
-};
-
-/* 16 bit int */
-
-static int get_int16(QEMUFile *f, void *pv, size_t size)
-{
- int16_t *v = pv;
- qemu_get_sbe16s(f, v);
- return 0;
-}
-
-static void put_int16(QEMUFile *f, void *pv, size_t size)
-{
- int16_t *v = pv;
- qemu_put_sbe16s(f, v);
-}
-
-const VMStateInfo vmstate_info_int16 = {
- .name = "int16",
- .get = get_int16,
- .put = put_int16,
-};
-
-/* 32 bit int */
-
-static int get_int32(QEMUFile *f, void *pv, size_t size)
-{
- int32_t *v = pv;
- qemu_get_sbe32s(f, v);
- return 0;
-}
-
-static void put_int32(QEMUFile *f, void *pv, size_t size)
-{
- int32_t *v = pv;
- qemu_put_sbe32s(f, v);
-}
-
-const VMStateInfo vmstate_info_int32 = {
- .name = "int32",
- .get = get_int32,
- .put = put_int32,
-};
-
-/* 32 bit int. See that the received value is the same than the one
- in the field */
-
-static int get_int32_equal(QEMUFile *f, void *pv, size_t size)
-{
- int32_t *v = pv;
- int32_t v2;
- qemu_get_sbe32s(f, &v2);
-
- if (*v == v2)
- return 0;
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_int32_equal = {
- .name = "int32 equal",
- .get = get_int32_equal,
- .put = put_int32,
-};
-
-/* 32 bit int. See that the received value is the less or the same
- than the one in the field */
-
-static int get_int32_le(QEMUFile *f, void *pv, size_t size)
-{
- int32_t *old = pv;
- int32_t new;
- qemu_get_sbe32s(f, &new);
-
- if (*old <= new)
- return 0;
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_int32_le = {
- .name = "int32 equal",
- .get = get_int32_le,
- .put = put_int32,
-};
-
-/* 64 bit int */
-
-static int get_int64(QEMUFile *f, void *pv, size_t size)
-{
- int64_t *v = pv;
- qemu_get_sbe64s(f, v);
- return 0;
-}
-
-static void put_int64(QEMUFile *f, void *pv, size_t size)
-{
- int64_t *v = pv;
- qemu_put_sbe64s(f, v);
-}
-
-const VMStateInfo vmstate_info_int64 = {
- .name = "int64",
- .get = get_int64,
- .put = put_int64,
-};
-
-/* 8 bit unsigned int */
-
-static int get_uint8(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t *v = pv;
- qemu_get_8s(f, v);
- return 0;
-}
-
-static void put_uint8(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t *v = pv;
- qemu_put_8s(f, v);
-}
-
-const VMStateInfo vmstate_info_uint8 = {
- .name = "uint8",
- .get = get_uint8,
- .put = put_uint8,
-};
-
-/* 16 bit unsigned int */
-
-static int get_uint16(QEMUFile *f, void *pv, size_t size)
-{
- uint16_t *v = pv;
- qemu_get_be16s(f, v);
- return 0;
-}
-
-static void put_uint16(QEMUFile *f, void *pv, size_t size)
-{
- uint16_t *v = pv;
- qemu_put_be16s(f, v);
-}
-
-const VMStateInfo vmstate_info_uint16 = {
- .name = "uint16",
- .get = get_uint16,
- .put = put_uint16,
-};
-
-/* 32 bit unsigned int */
-
-static int get_uint32(QEMUFile *f, void *pv, size_t size)
-{
- uint32_t *v = pv;
- qemu_get_be32s(f, v);
- return 0;
-}
-
-static void put_uint32(QEMUFile *f, void *pv, size_t size)
-{
- uint32_t *v = pv;
- qemu_put_be32s(f, v);
-}
-
-const VMStateInfo vmstate_info_uint32 = {
- .name = "uint32",
- .get = get_uint32,
- .put = put_uint32,
-};
-
-/* 32 bit uint. See that the received value is the same than the one
- in the field */
-
-static int get_uint32_equal(QEMUFile *f, void *pv, size_t size)
-{
- uint32_t *v = pv;
- uint32_t v2;
- qemu_get_be32s(f, &v2);
-
- if (*v == v2) {
- return 0;
- }
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_uint32_equal = {
- .name = "uint32 equal",
- .get = get_uint32_equal,
- .put = put_uint32,
-};
-
-/* 64 bit unsigned int */
-
-static int get_uint64(QEMUFile *f, void *pv, size_t size)
-{
- uint64_t *v = pv;
- qemu_get_be64s(f, v);
- return 0;
-}
-
-static void put_uint64(QEMUFile *f, void *pv, size_t size)
-{
- uint64_t *v = pv;
- qemu_put_be64s(f, v);
-}
-
-const VMStateInfo vmstate_info_uint64 = {
- .name = "uint64",
- .get = get_uint64,
- .put = put_uint64,
-};
-
-/* 64 bit unsigned int. See that the received value is the same than the one
- in the field */
-
-static int get_uint64_equal(QEMUFile *f, void *pv, size_t size)
-{
- uint64_t *v = pv;
- uint64_t v2;
- qemu_get_be64s(f, &v2);
-
- if (*v == v2) {
- return 0;
- }
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_uint64_equal = {
- .name = "int64 equal",
- .get = get_uint64_equal,
- .put = put_uint64,
-};
-
-/* 8 bit int. See that the received value is the same than the one
- in the field */
-
-static int get_uint8_equal(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t *v = pv;
- uint8_t v2;
- qemu_get_8s(f, &v2);
-
- if (*v == v2)
- return 0;
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_uint8_equal = {
- .name = "uint8 equal",
- .get = get_uint8_equal,
- .put = put_uint8,
-};
-
-/* 16 bit unsigned int int. See that the received value is the same than the one
- in the field */
-
-static int get_uint16_equal(QEMUFile *f, void *pv, size_t size)
-{
- uint16_t *v = pv;
- uint16_t v2;
- qemu_get_be16s(f, &v2);
-
- if (*v == v2)
- return 0;
- return -EINVAL;
-}
-
-const VMStateInfo vmstate_info_uint16_equal = {
- .name = "uint16 equal",
- .get = get_uint16_equal,
- .put = put_uint16,
-};
-
-/* floating point */
-
-static int get_float64(QEMUFile *f, void *pv, size_t size)
-{
- float64 *v = pv;
-
- *v = make_float64(qemu_get_be64(f));
- return 0;
-}
-
-static void put_float64(QEMUFile *f, void *pv, size_t size)
-{
- uint64_t *v = pv;
-
- qemu_put_be64(f, float64_val(*v));
-}
-
-const VMStateInfo vmstate_info_float64 = {
- .name = "float64",
- .get = get_float64,
- .put = put_float64,
-};
-
-/* timers */
+/* VMState timer support.
+ * Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c
+ */
static int get_timer(QEMUFile *f, void *pv, size_t size)
{
@@ -1362,100 +214,6 @@ const VMStateInfo vmstate_info_timer = {
.put = put_timer,
};
-/* uint8_t buffers */
-
-static int get_buffer(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t *v = pv;
- qemu_get_buffer(f, v, size);
- return 0;
-}
-
-static void put_buffer(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t *v = pv;
- qemu_put_buffer(f, v, size);
-}
-
-const VMStateInfo vmstate_info_buffer = {
- .name = "buffer",
- .get = get_buffer,
- .put = put_buffer,
-};
-
-/* unused buffers: space that was used for some fields that are
- not useful anymore */
-
-static int get_unused_buffer(QEMUFile *f, void *pv, size_t size)
-{
- uint8_t buf[1024];
- int block_len;
-
- while (size > 0) {
- block_len = MIN(sizeof(buf), size);
- size -= block_len;
- qemu_get_buffer(f, buf, block_len);
- }
- return 0;
-}
-
-static void put_unused_buffer(QEMUFile *f, void *pv, size_t size)
-{
- static const uint8_t buf[1024];
- int block_len;
-
- while (size > 0) {
- block_len = MIN(sizeof(buf), size);
- size -= block_len;
- qemu_put_buffer(f, buf, block_len);
- }
-}
-
-const VMStateInfo vmstate_info_unused_buffer = {
- .name = "unused_buffer",
- .get = get_unused_buffer,
- .put = put_unused_buffer,
-};
-
-/* bitmaps (as defined by bitmap.h). Note that size here is the size
- * of the bitmap in bits. The on-the-wire format of a bitmap is 64
- * bit words with the bits in big endian order. The in-memory format
- * is an array of 'unsigned long', which may be either 32 or 64 bits.
- */
-/* This is the number of 64 bit words sent over the wire */
-#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64)
-static int get_bitmap(QEMUFile *f, void *pv, size_t size)
-{
- unsigned long *bmp = pv;
- int i, idx = 0;
- for (i = 0; i < BITS_TO_U64S(size); i++) {
- uint64_t w = qemu_get_be64(f);
- bmp[idx++] = w;
- if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
- bmp[idx++] = w >> 32;
- }
- }
- return 0;
-}
-
-static void put_bitmap(QEMUFile *f, void *pv, size_t size)
-{
- unsigned long *bmp = pv;
- int i, idx = 0;
- for (i = 0; i < BITS_TO_U64S(size); i++) {
- uint64_t w = bmp[idx++];
- if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
- w |= ((uint64_t)bmp[idx++]) << 32;
- }
- qemu_put_be64(f, w);
- }
-}
-
-const VMStateInfo vmstate_info_bitmap = {
- .name = "bitmap",
- .get = get_bitmap,
- .put = put_bitmap,
-};
typedef struct CompatEntry {
char idstr[256];
@@ -1502,8 +260,9 @@ static int calculate_compat_instance_id(const char *idstr)
int instance_id = 0;
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
- if (!se->compat)
+ if (!se->compat) {
continue;
+ }
if (strcmp(idstr, se->compat->idstr) == 0
&& instance_id <= se->compat->instance_id) {
@@ -1668,142 +427,6 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
}
}
-static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque);
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque);
-
-int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque, int version_id)
-{
- VMStateField *field = vmsd->fields;
- int ret;
-
- if (version_id > vmsd->version_id) {
- return -EINVAL;
- }
- if (version_id < vmsd->minimum_version_id_old) {
- return -EINVAL;
- }
- if (version_id < vmsd->minimum_version_id) {
- return vmsd->load_state_old(f, opaque, version_id);
- }
- if (vmsd->pre_load) {
- int ret = vmsd->pre_load(opaque);
- if (ret)
- return ret;
- }
- while(field->name) {
- if ((field->field_exists &&
- field->field_exists(opaque, version_id)) ||
- (!field->field_exists &&
- field->version_id <= version_id)) {
- void *base_addr = opaque + field->offset;
- int i, n_elems = 1;
- int size = field->size;
-
- if (field->flags & VMS_VBUFFER) {
- size = *(int32_t *)(opaque+field->size_offset);
- if (field->flags & VMS_MULTIPLY) {
- size *= field->size;
- }
- }
- if (field->flags & VMS_ARRAY) {
- n_elems = field->num;
- } else if (field->flags & VMS_VARRAY_INT32) {
- n_elems = *(int32_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT32) {
- n_elems = *(uint32_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT16) {
- n_elems = *(uint16_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT8) {
- n_elems = *(uint8_t *)(opaque+field->num_offset);
- }
- if (field->flags & VMS_POINTER) {
- base_addr = *(void **)base_addr + field->start;
- }
- for (i = 0; i < n_elems; i++) {
- void *addr = base_addr + size * i;
-
- if (field->flags & VMS_ARRAY_OF_POINTER) {
- addr = *(void **)addr;
- }
- if (field->flags & VMS_STRUCT) {
- ret = vmstate_load_state(f, field->vmsd, addr, field->vmsd->version_id);
- } else {
- ret = field->info->get(f, addr, size);
-
- }
- if (ret < 0) {
- return ret;
- }
- }
- }
- field++;
- }
- ret = vmstate_subsection_load(f, vmsd, opaque);
- if (ret != 0) {
- return ret;
- }
- if (vmsd->post_load) {
- return vmsd->post_load(opaque, version_id);
- }
- return 0;
-}
-
-void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque)
-{
- VMStateField *field = vmsd->fields;
-
- if (vmsd->pre_save) {
- vmsd->pre_save(opaque);
- }
- while(field->name) {
- if (!field->field_exists ||
- field->field_exists(opaque, vmsd->version_id)) {
- void *base_addr = opaque + field->offset;
- int i, n_elems = 1;
- int size = field->size;
-
- if (field->flags & VMS_VBUFFER) {
- size = *(int32_t *)(opaque+field->size_offset);
- if (field->flags & VMS_MULTIPLY) {
- size *= field->size;
- }
- }
- if (field->flags & VMS_ARRAY) {
- n_elems = field->num;
- } else if (field->flags & VMS_VARRAY_INT32) {
- n_elems = *(int32_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT32) {
- n_elems = *(uint32_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT16) {
- n_elems = *(uint16_t *)(opaque+field->num_offset);
- } else if (field->flags & VMS_VARRAY_UINT8) {
- n_elems = *(uint8_t *)(opaque+field->num_offset);
- }
- if (field->flags & VMS_POINTER) {
- base_addr = *(void **)base_addr + field->start;
- }
- for (i = 0; i < n_elems; i++) {
- void *addr = base_addr + size * i;
-
- if (field->flags & VMS_ARRAY_OF_POINTER) {
- addr = *(void **)addr;
- }
- if (field->flags & VMS_STRUCT) {
- vmstate_save_state(f, field->vmsd, addr);
- } else {
- field->info->put(f, addr, size);
- }
- }
- }
- field++;
- }
- vmstate_subsection_save(f, vmsd, opaque);
-}
-
static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
{
if (!se->vmsd) { /* Old style */
@@ -1818,20 +441,9 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
se->ops->save_state(f, se->opaque);
return;
}
- vmstate_save_state(f,se->vmsd, se->opaque);
+ vmstate_save_state(f, se->vmsd, se->opaque);
}
-#define QEMU_VM_FILE_MAGIC 0x5145564d
-#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
-#define QEMU_VM_FILE_VERSION 0x00000003
-
-#define QEMU_VM_EOF 0x00
-#define QEMU_VM_SECTION_START 0x01
-#define QEMU_VM_SECTION_PART 0x02
-#define QEMU_VM_SECTION_END 0x03
-#define QEMU_VM_SECTION_FULL 0x04
-#define QEMU_VM_SUBSECTION 0x05
-
bool qemu_savevm_state_blocked(Error **errp)
{
SaveStateEntry *se;
@@ -1857,7 +469,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
}
se->ops->set_params(params, se->opaque);
}
-
+
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
@@ -1970,7 +582,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
int len;
if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
- continue;
+ continue;
}
trace_savevm_section_start();
/* Section type */
@@ -2115,79 +727,6 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
return NULL;
}
-static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
-{
- while(sub && sub->needed) {
- if (strcmp(idstr, sub->vmsd->name) == 0) {
- return sub->vmsd;
- }
- sub++;
- }
- return NULL;
-}
-
-static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque)
-{
- while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
- char idstr[256];
- int ret;
- uint8_t version_id, len, size;
- const VMStateDescription *sub_vmsd;
-
- len = qemu_peek_byte(f, 1);
- if (len < strlen(vmsd->name) + 1) {
- /* subsection name has be be "section_name/a" */
- return 0;
- }
- size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
- if (size != len) {
- return 0;
- }
- idstr[size] = 0;
-
- if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
- /* it don't have a valid subsection name */
- return 0;
- }
- sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
- if (sub_vmsd == NULL) {
- return -ENOENT;
- }
- qemu_file_skip(f, 1); /* subsection */
- qemu_file_skip(f, 1); /* len */
- qemu_file_skip(f, len); /* idstr */
- version_id = qemu_get_be32(f);
-
- ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
- if (ret) {
- return ret;
- }
- }
- return 0;
-}
-
-static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
- void *opaque)
-{
- const VMStateSubsection *sub = vmsd->subsections;
-
- while (sub && sub->needed) {
- if (sub->needed(opaque)) {
- const VMStateDescription *vmsd = sub->vmsd;
- uint8_t len;
-
- qemu_put_byte(f, QEMU_VM_SUBSECTION);
- len = strlen(vmsd->name);
- qemu_put_byte(f, len);
- qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
- qemu_put_be32(f, vmsd->version_id);
- vmstate_save_state(f, vmsd, opaque);
- }
- sub++;
- }
-}
-
typedef struct LoadStateEntry {
QLIST_ENTRY(LoadStateEntry) entry;
SaveStateEntry *se;
@@ -2209,16 +748,18 @@ int qemu_loadvm_state(QEMUFile *f)
}
v = qemu_get_be32(f);
- if (v != QEMU_VM_FILE_MAGIC)
+ if (v != QEMU_VM_FILE_MAGIC) {
return -EINVAL;
+ }
v = qemu_get_be32(f);
if (v == QEMU_VM_FILE_VERSION_COMPAT) {
fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n");
return -ENOTSUP;
}
- if (v != QEMU_VM_FILE_VERSION)
+ if (v != QEMU_VM_FILE_VERSION) {
return -ENOTSUP;
+ }
while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
uint32_t instance_id, version_id, section_id;
@@ -2337,8 +878,7 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
bs = NULL;
while ((bs = bdrv_next(bs))) {
if (bdrv_can_snapshot(bs) &&
- bdrv_snapshot_find(bs, snapshot, name) >= 0)
- {
+ bdrv_snapshot_find(bs, snapshot, name) >= 0) {
bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (error_is_set(&err)) {
monitor_printf(mon,
@@ -2448,8 +988,9 @@ void do_savevm(Monitor *mon, const QDict *qdict)
}
the_end:
- if (saved_vm_running)
+ if (saved_vm_running) {
vm_start();
+ }
}
void qmp_xen_save_devices_state(const char *filename, Error **errp)
@@ -2473,8 +1014,9 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp)
}
the_end:
- if (saved_vm_running)
+ if (saved_vm_running) {
vm_start();
+ }
}
int load_vmstate(const char *name)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 750e9fb552..9b3de4c7c3 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -247,7 +247,7 @@ def c_var(name, protect=True):
'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
# namespace pollution:
- polluted_words = set(['unix'])
+ polluted_words = set(['unix', 'errno'])
if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
return "q_" + name
return name.replace('-', '_').lstrip("*")
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 6d147a7ece..16439c5860 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -11,7 +11,6 @@
typedef struct SpiceCharDriver {
CharDriverState* chr;
SpiceCharDeviceInstance sin;
- char *subtype;
bool active;
bool blocked;
const uint8_t *datapos;
@@ -223,6 +222,15 @@ static void spice_chr_set_fe_open(struct CharDriverState *chr, int fe_open)
}
}
+static void spice_chr_fe_event(struct CharDriverState *chr, int event)
+{
+#if SPICE_SERVER_VERSION >= 0x000c02
+ SpiceCharDriver *s = chr->opaque;
+
+ spice_server_port_event(&s->sin, event);
+#endif
+}
+
static void print_allowed_subtypes(void)
{
const char** psubtype;
@@ -256,6 +264,7 @@ static CharDriverState *chr_open(const char *subtype)
chr->chr_close = spice_chr_close;
chr->chr_set_fe_open = spice_chr_set_fe_open;
chr->explicit_be_open = true;
+ chr->chr_fe_event = spice_chr_fe_event;
QLIST_INSERT_HEAD(&spice_chars, s, next);
diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs
index 356fbfcdfd..dcd167e0d8 100644
--- a/target-arm/Makefile.objs
+++ b/target-arm/Makefile.objs
@@ -1,8 +1,11 @@
obj-y += arm-semi.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o
+obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o
+obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += neon_helper.o iwmmxt_helper.o
obj-y += gdbstub.o
-obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o gdbstub64.o
+obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o
+obj-y += crypto_helper.o
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index f32178a9db..afbd4222c5 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -139,6 +139,7 @@ typedef struct ARMCPU {
uint32_t ccsidr[16];
uint32_t reset_cbar;
uint32_t reset_auxcr;
+ bool reset_hivecs;
} ARMCPU;
#define TYPE_AARCH64_CPU "aarch64-cpu"
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 0635e78ec2..52efd5d66f 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -21,6 +21,7 @@
#include "cpu.h"
#include "qemu-common.h"
#include "hw/qdev-properties.h"
+#include "qapi/qmp/qerror.h"
#if !defined(CONFIG_USER_ONLY)
#include "hw/loader.h"
#endif
@@ -88,6 +89,12 @@ static void arm_cpu_reset(CPUState *s)
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
/* 64 bit CPUs always start in 64 bit mode */
env->aarch64 = 1;
+#if defined(CONFIG_USER_ONLY)
+ env->pstate = PSTATE_MODE_EL0t;
+#else
+ env->pstate = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F
+ | PSTATE_MODE_EL1h;
+#endif
}
#if defined(CONFIG_USER_ONLY)
@@ -120,6 +127,11 @@ static void arm_cpu_reset(CPUState *s)
env->regs[15] = pc & ~1;
}
}
+
+ if (env->cp15.c1_sys & (1 << 13)) {
+ env->regs[15] = 0xFFFF0000;
+ }
+
env->vfp.xregs[ARM_VFP_FPEXC] = 0;
#endif
set_flush_to_zero(1, &env->vfp.standard_fp_status);
@@ -231,6 +243,27 @@ static void arm_cpu_initfn(Object *obj)
}
}
+static Property arm_cpu_reset_cbar_property =
+ DEFINE_PROP_UINT32("reset-cbar", ARMCPU, reset_cbar, 0);
+
+static Property arm_cpu_reset_hivecs_property =
+ DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false);
+
+static void arm_cpu_post_init(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ if (arm_feature(&cpu->env, ARM_FEATURE_CBAR)) {
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property,
+ &error_abort);
+ }
+
+ if (!arm_feature(&cpu->env, ARM_FEATURE_M)) {
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_hivecs_property,
+ &error_abort);
+ }
+}
+
static void arm_cpu_finalizefn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@@ -249,6 +282,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_V7);
set_feature(env, ARM_FEATURE_ARM_DIV);
set_feature(env, ARM_FEATURE_LPAE);
+ set_feature(env, ARM_FEATURE_V8_AES);
}
if (arm_feature(env, ARM_FEATURE_V7)) {
set_feature(env, ARM_FEATURE_VAPA);
@@ -290,6 +324,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
set_feature(env, ARM_FEATURE_PXN);
}
+ if (cpu->reset_hivecs) {
+ cpu->reset_sctlr |= (1 << 13);
+ }
+
register_cp_regs_for_features(cpu);
arm_cpu_register_gdb_regs_for_features(cpu);
@@ -616,6 +654,7 @@ static void cortex_a9_initfn(Object *obj)
* and valid configurations; we don't model A9UP).
*/
set_feature(&cpu->env, ARM_FEATURE_V7MP);
+ set_feature(&cpu->env, ARM_FEATURE_CBAR);
cpu->midr = 0x410fc090;
cpu->reset_fpsid = 0x41033090;
cpu->mvfr0 = 0x11110222;
@@ -638,15 +677,7 @@ static void cortex_a9_initfn(Object *obj)
cpu->clidr = (1 << 27) | (1 << 24) | 3;
cpu->ccsidr[0] = 0xe00fe015; /* 16k L1 dcache. */
cpu->ccsidr[1] = 0x200fe015; /* 16k L1 icache. */
- {
- ARMCPRegInfo cbar = {
- .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4,
- .opc2 = 0, .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
- .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
- };
- define_one_arm_cp_reg(cpu, &cbar);
- define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
- }
+ define_arm_cp_regs(cpu, cortexa9_cp_reginfo);
}
#ifndef CONFIG_USER_ONLY
@@ -685,6 +716,7 @@ static void cortex_a15_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_ARM_DIV);
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS);
+ set_feature(&cpu->env, ARM_FEATURE_CBAR);
set_feature(&cpu->env, ARM_FEATURE_LPAE);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15;
cpu->midr = 0x412fc0f1;
@@ -945,6 +977,7 @@ static const ARMCPUInfo arm_cpus[] = {
{ .name = "any", .initfn = arm_any_initfn },
#endif
#endif
+ { .name = NULL }
};
static Property arm_cpu_properties[] = {
@@ -999,6 +1032,7 @@ static const TypeInfo arm_cpu_type_info = {
.parent = TYPE_CPU,
.instance_size = sizeof(ARMCPU),
.instance_init = arm_cpu_initfn,
+ .instance_post_init = arm_cpu_post_init,
.instance_finalize = arm_cpu_finalizefn,
.abstract = true,
.class_size = sizeof(ARMCPUClass),
@@ -1007,11 +1041,13 @@ static const TypeInfo arm_cpu_type_info = {
static void arm_cpu_register_types(void)
{
- int i;
+ const ARMCPUInfo *info = arm_cpus;
type_register_static(&arm_cpu_type_info);
- for (i = 0; i < ARRAY_SIZE(arm_cpus); i++) {
- cpu_register(&arm_cpus[i]);
+
+ while (info->name) {
+ cpu_register(info);
+ info++;
}
}
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index c3f007fc53..198b6b8d4e 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -66,6 +66,18 @@
/* ARM-specific interrupt pending bits. */
#define CPU_INTERRUPT_FIQ CPU_INTERRUPT_TGT_EXT_1
+/* The usual mapping for an AArch64 system register to its AArch32
+ * counterpart is for the 32 bit world to have access to the lower
+ * half only (with writes leaving the upper half untouched). It's
+ * therefore useful to be able to pass TCG the offset of the least
+ * significant half of a uint64_t struct member.
+ */
+#ifdef HOST_WORDS_BIGENDIAN
+#define offsetoflow32(S, M) (offsetof(S, M) + sizeof(uint32_t))
+#else
+#define offsetoflow32(S, M) offsetof(S, M)
+#endif
+
/* Meanings of the ARMCPU object's two inbound GPIO lines */
#define ARM_CPU_IRQ 0
#define ARM_CPU_FIQ 1
@@ -113,8 +125,15 @@ typedef struct CPUARMState {
/* Regs for A64 mode. */
uint64_t xregs[32];
uint64_t pc;
- /* TODO: pstate doesn't correspond to an architectural register;
- * it would be better modelled as the underlying fields.
+ /* PSTATE isn't an architectural register for ARMv8. However, it is
+ * convenient for us to assemble the underlying state into a 32 bit format
+ * identical to the architectural format used for the SPSR. (This is also
+ * what the Linux kernel's 'pstate' field in signal handlers and KVM's
+ * 'pstate' register are.) Of the PSTATE bits:
+ * NZCV are kept in the split out env->CF/VF/NF/ZF, (which have the same
+ * semantics as for AArch32, as described in the comments on each field)
+ * nRW (also known as M[4]) is kept, inverted, in env->aarch64
+ * all other bits are stored in their correct places in env->pstate
*/
uint32_t pstate;
uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
@@ -181,9 +200,9 @@ typedef struct CPUARMState {
uint32_t c12_vbar; /* vector base address register */
uint32_t c13_fcse; /* FCSE PID. */
uint32_t c13_context; /* Context ID. */
- uint32_t c13_tls1; /* User RW Thread register. */
- uint32_t c13_tls2; /* User RO Thread register. */
- uint32_t c13_tls3; /* Privileged Thread register. */
+ uint64_t tpidr_el0; /* User RW Thread register. */
+ uint64_t tpidrro_el0; /* User RO Thread register. */
+ uint64_t tpidr_el1; /* Privileged Thread register. */
uint32_t c14_cntfrq; /* Counter Frequency register */
uint32_t c14_cntkctl; /* Timer Control register */
ARMGenericTimer c14_timer[NUM_GTIMERS];
@@ -259,11 +278,11 @@ typedef struct CPUARMState {
float_status fp_status;
float_status standard_fp_status;
} vfp;
- uint32_t exclusive_addr;
- uint32_t exclusive_val;
- uint32_t exclusive_high;
+ uint64_t exclusive_addr;
+ uint64_t exclusive_val;
+ uint64_t exclusive_high;
#if defined(CONFIG_USER_ONLY)
- uint32_t exclusive_test;
+ uint64_t exclusive_test;
uint32_t exclusive_info;
#endif
@@ -309,15 +328,6 @@ static inline bool is_a64(CPUARMState *env)
return env->aarch64;
}
-#define PSTATE_N_SHIFT 3
-#define PSTATE_N (1 << PSTATE_N_SHIFT)
-#define PSTATE_Z_SHIFT 2
-#define PSTATE_Z (1 << PSTATE_Z_SHIFT)
-#define PSTATE_C_SHIFT 1
-#define PSTATE_C (1 << PSTATE_C_SHIFT)
-#define PSTATE_V_SHIFT 0
-#define PSTATE_V (1 << PSTATE_V_SHIFT)
-
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */
@@ -352,6 +362,56 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
/* Execution state bits. MRS read as zero, MSR writes ignored. */
#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
+/* Bit definitions for ARMv8 SPSR (PSTATE) format.
+ * Only these are valid when in AArch64 mode; in
+ * AArch32 mode SPSRs are basically CPSR-format.
+ */
+#define PSTATE_M (0xFU)
+#define PSTATE_nRW (1U << 4)
+#define PSTATE_F (1U << 6)
+#define PSTATE_I (1U << 7)
+#define PSTATE_A (1U << 8)
+#define PSTATE_D (1U << 9)
+#define PSTATE_IL (1U << 20)
+#define PSTATE_SS (1U << 21)
+#define PSTATE_V (1U << 28)
+#define PSTATE_C (1U << 29)
+#define PSTATE_Z (1U << 30)
+#define PSTATE_N (1U << 31)
+#define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V)
+#define CACHED_PSTATE_BITS (PSTATE_NZCV)
+/* Mode values for AArch64 */
+#define PSTATE_MODE_EL3h 13
+#define PSTATE_MODE_EL3t 12
+#define PSTATE_MODE_EL2h 9
+#define PSTATE_MODE_EL2t 8
+#define PSTATE_MODE_EL1h 5
+#define PSTATE_MODE_EL1t 4
+#define PSTATE_MODE_EL0t 0
+
+/* Return the current PSTATE value. For the moment we don't support 32<->64 bit
+ * interprocessing, so we don't attempt to sync with the cpsr state used by
+ * the 32 bit decoder.
+ */
+static inline uint32_t pstate_read(CPUARMState *env)
+{
+ int ZF;
+
+ ZF = (env->ZF == 0);
+ return (env->NF & 0x80000000) | (ZF << 30)
+ | (env->CF << 29) | ((env->VF & 0x80000000) >> 3)
+ | env->pstate;
+}
+
+static inline void pstate_write(CPUARMState *env, uint32_t val)
+{
+ env->ZF = (~val) & PSTATE_Z;
+ env->NF = val;
+ env->CF = (val >> 29) & 1;
+ env->VF = (val << 3) & 0x80000000;
+ env->pstate = val & ~CACHED_PSTATE_BITS;
+}
+
/* Return the current CPSR value. */
uint32_t cpsr_read(CPUARMState *env);
/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
@@ -399,6 +459,43 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
uint32_t vfp_get_fpscr(CPUARMState *env);
void vfp_set_fpscr(CPUARMState *env, uint32_t val);
+/* For A64 the FPSCR is split into two logically distinct registers,
+ * FPCR and FPSR. However since they still use non-overlapping bits
+ * we store the underlying state in fpscr and just mask on read/write.
+ */
+#define FPSR_MASK 0xf800009f
+#define FPCR_MASK 0x07f79f00
+static inline uint32_t vfp_get_fpsr(CPUARMState *env)
+{
+ return vfp_get_fpscr(env) & FPSR_MASK;
+}
+
+static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val)
+{
+ uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK);
+ vfp_set_fpscr(env, new_fpscr);
+}
+
+static inline uint32_t vfp_get_fpcr(CPUARMState *env)
+{
+ return vfp_get_fpscr(env) & FPCR_MASK;
+}
+
+static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val)
+{
+ uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK);
+ vfp_set_fpscr(env, new_fpscr);
+}
+
+enum arm_fprounding {
+ FPROUNDING_TIEEVEN,
+ FPROUNDING_POSINF,
+ FPROUNDING_NEGINF,
+ FPROUNDING_ZERO,
+ FPROUNDING_TIEAWAY,
+ FPROUNDING_ODD
+};
+
enum arm_cpu_mode {
ARM_CPU_MODE_USR = 0x10,
ARM_CPU_MODE_FIQ = 0x11,
@@ -467,6 +564,8 @@ enum arm_features {
ARM_FEATURE_LPAE, /* has Large Physical Address Extension */
ARM_FEATURE_V8,
ARM_FEATURE_AARCH64, /* supports 64 bit mode */
+ ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */
+ ARM_FEATURE_CBAR, /* has cp15 CBAR */
};
static inline int arm_feature(CPUARMState *env, int feature)
@@ -494,18 +593,43 @@ void armv7m_nvic_complete_irq(void *opaque, int irq);
* or via MRRC/MCRR?)
* We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field.
* (In this case crn and opc2 should be zero.)
+ * For AArch64, there is no 32/64 bit size distinction;
+ * instead all registers have a 2 bit op0, 3 bit op1 and op2,
+ * and 4 bit CRn and CRm. The encoding patterns are chosen
+ * to be easy to convert to and from the KVM encodings, and also
+ * so that the hashtable can contain both AArch32 and AArch64
+ * registers (to allow for interprocessing where we might run
+ * 32 bit code on a 64 bit core).
*/
+/* This bit is private to our hashtable cpreg; in KVM register
+ * IDs the AArch64/32 distinction is the KVM_REG_ARM/ARM64
+ * in the upper bits of the 64 bit ID.
+ */
+#define CP_REG_AA64_SHIFT 28
+#define CP_REG_AA64_MASK (1 << CP_REG_AA64_SHIFT)
+
#define ENCODE_CP_REG(cp, is64, crn, crm, opc1, opc2) \
(((cp) << 16) | ((is64) << 15) | ((crn) << 11) | \
((crm) << 7) | ((opc1) << 3) | (opc2))
+#define ENCODE_AA64_CP_REG(cp, crn, crm, op0, op1, op2) \
+ (CP_REG_AA64_MASK | \
+ ((cp) << CP_REG_ARM_COPROC_SHIFT) | \
+ ((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \
+ ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \
+ ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \
+ ((crm) << CP_REG_ARM64_SYSREG_CRM_SHIFT) | \
+ ((op2) << CP_REG_ARM64_SYSREG_OP2_SHIFT))
+
/* Convert a full 64 bit KVM register ID to the truncated 32 bit
* version used as a key for the coprocessor register hashtable
*/
static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid)
{
uint32_t cpregid = kvmid;
- if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) {
+ if ((kvmid & CP_REG_ARCH_MASK) == CP_REG_ARM64) {
+ cpregid |= CP_REG_AA64_MASK;
+ } else if ((kvmid & CP_REG_SIZE_MASK) == CP_REG_SIZE_U64) {
cpregid |= (1 << 15);
}
return cpregid;
@@ -516,11 +640,18 @@ static inline uint32_t kvm_to_cpreg_id(uint64_t kvmid)
*/
static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
{
- uint64_t kvmid = cpregid & ~(1 << 15);
- if (cpregid & (1 << 15)) {
- kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM;
+ uint64_t kvmid;
+
+ if (cpregid & CP_REG_AA64_MASK) {
+ kvmid = cpregid & ~CP_REG_AA64_MASK;
+ kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM64;
} else {
- kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM;
+ kvmid = cpregid & ~(1 << 15);
+ if (cpregid & (1 << 15)) {
+ kvmid |= CP_REG_SIZE_U64 | CP_REG_ARM;
+ } else {
+ kvmid |= CP_REG_SIZE_U32 | CP_REG_ARM;
+ }
}
return kvmid;
}
@@ -550,12 +681,28 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
#define ARM_CP_IO 64
#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8))
#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8))
-#define ARM_LAST_SPECIAL ARM_CP_WFI
+#define ARM_CP_NZCV (ARM_CP_SPECIAL | (3 << 8))
+#define ARM_LAST_SPECIAL ARM_CP_NZCV
/* Used only as a terminator for ARMCPRegInfo lists */
#define ARM_CP_SENTINEL 0xffff
/* Mask of only the flag bits in a type field */
#define ARM_CP_FLAG_MASK 0x7f
+/* Valid values for ARMCPRegInfo state field, indicating which of
+ * the AArch32 and AArch64 execution states this register is visible in.
+ * If the reginfo doesn't explicitly specify then it is AArch32 only.
+ * If the reginfo is declared to be visible in both states then a second
+ * reginfo is synthesised for the AArch32 view of the AArch64 register,
+ * such that the AArch32 view is the lower 32 bits of the AArch64 one.
+ * Note that we rely on the values of these enums as we iterate through
+ * the various states in some places.
+ */
+enum {
+ ARM_CP_STATE_AA32 = 0,
+ ARM_CP_STATE_AA64 = 1,
+ ARM_CP_STATE_BOTH = 2,
+};
+
/* Return true if cptype is a valid type field. This is used to try to
* catch errors where the sentinel has been accidentally left off the end
* of a list of registers.
@@ -577,6 +724,8 @@ static inline bool cptype_valid(int cptype)
* (ie anything visible in PL2 is visible in S-PL1, some things are only
* visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the
* terminology a little and call this PL3.
+ * In AArch64 things are somewhat simpler as the PLx bits line up exactly
+ * with the ELx exception levels.
*
* If access permissions for a register are more complex than can be
* described with these bits, then use a laxer set of restrictions, and
@@ -598,6 +747,10 @@ static inline bool cptype_valid(int cptype)
static inline int arm_current_pl(CPUARMState *env)
{
+ if (env->aarch64) {
+ return extract32(env->pstate, 2, 2);
+ }
+
if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) {
return 0;
}
@@ -635,12 +788,22 @@ struct ARMCPRegInfo {
* then behave differently on read/write if necessary.
* For 64 bit registers, only crm and opc1 are relevant; crn and opc2
* must both be zero.
+ * For AArch64-visible registers, opc0 is also used.
+ * Since there are no "coprocessors" in AArch64, cp is purely used as a
+ * way to distinguish (for KVM's benefit) guest-visible system registers
+ * from demuxed ones provided to preserve the "no side effects on
+ * KVM register read/write from QEMU" semantics. cp==0x13 is guest
+ * visible (to match KVM's encoding); cp==0 will be converted to
+ * cp==0x13 when the ARMCPRegInfo is registered, for convenience.
*/
uint8_t cp;
uint8_t crn;
uint8_t crm;
+ uint8_t opc0;
uint8_t opc1;
uint8_t opc2;
+ /* Execution state in which this register is visible: ARM_CP_STATE_* */
+ int state;
/* Register type: ARM_CP_* bits/values */
int type;
/* Access rights: PL*_[RW] */
@@ -712,7 +875,7 @@ static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs)
{
define_one_arm_cp_reg_with_opaque(cpu, regs, 0);
}
-const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp);
+const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp);
/* CPWriteFn that can be used to implement writes-ignored behaviour */
int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -720,10 +883,15 @@ int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri,
/* CPReadFn that can be used for read-as-zero behaviour */
int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value);
-static inline bool cp_access_ok(CPUARMState *env,
+/* CPResetFn that does nothing, for use if no reset is required even
+ * if fieldoffset is non zero.
+ */
+void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque);
+
+static inline bool cp_access_ok(int current_pl,
const ARMCPRegInfo *ri, int isread)
{
- return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1;
+ return (ri->access >> ((current_pl * 2) + isread)) & 1;
}
/**
diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
index 3e99c2140a..a639c2e476 100644
--- a/target-arm/cpu64.c
+++ b/target-arm/cpu64.c
@@ -58,6 +58,7 @@ static const ARMCPUInfo aarch64_cpus[] = {
#ifdef CONFIG_USER_ONLY
{ .name = "any", .initfn = aarch64_any_initfn },
#endif
+ { .name = NULL }
};
static void aarch64_cpu_initfn(Object *obj)
@@ -68,11 +69,22 @@ static void aarch64_cpu_finalizefn(Object *obj)
{
}
+static void aarch64_cpu_set_pc(CPUState *cs, vaddr value)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ /*
+ * TODO: this will need updating for system emulation,
+ * when the core may be in AArch32 mode.
+ */
+ cpu->env.pc = value;
+}
+
static void aarch64_cpu_class_init(ObjectClass *oc, void *data)
{
CPUClass *cc = CPU_CLASS(oc);
cc->dump_state = aarch64_cpu_dump_state;
+ cc->set_pc = aarch64_cpu_set_pc;
cc->gdb_read_register = aarch64_cpu_gdb_read_register;
cc->gdb_write_register = aarch64_cpu_gdb_write_register;
cc->gdb_num_core_regs = 34;
@@ -107,11 +119,13 @@ static const TypeInfo aarch64_cpu_type_info = {
static void aarch64_cpu_register_types(void)
{
- int i;
+ const ARMCPUInfo *info = aarch64_cpus;
type_register_static(&aarch64_cpu_type_info);
- for (i = 0; i < ARRAY_SIZE(aarch64_cpus); i++) {
- aarch64_cpu_register(&aarch64_cpus[i]);
+
+ while (info->name) {
+ aarch64_cpu_register(info);
+ info++;
}
}
diff --git a/target-arm/crypto_helper.c b/target-arm/crypto_helper.c
new file mode 100644
index 0000000000..f94be69ac5
--- /dev/null
+++ b/target-arm/crypto_helper.c
@@ -0,0 +1,281 @@
+/*
+ * crypto_helper.c - emulate v8 Crypto Extensions instructions
+ *
+ * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@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.
+ */
+
+#include <stdlib.h>
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "helper.h"
+
+union AES_STATE {
+ uint8_t bytes[16];
+ uint32_t cols[4];
+ uint64_t l[2];
+};
+
+void HELPER(crypto_aese)(CPUARMState *env, uint32_t rd, uint32_t rm,
+ uint32_t decrypt)
+{
+ static uint8_t const sbox[][256] = { {
+ /* S-box for encryption */
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+ }, {
+ /* S-box for decryption */
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
+ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
+ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
+ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
+ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
+ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
+ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
+ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
+ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
+ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
+ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
+ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
+ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
+ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
+ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
+ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+ } };
+ static uint8_t const shift[][16] = {
+ /* ShiftRows permutation vector for encryption */
+ { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 },
+ /* ShiftRows permutation vector for decryption */
+ { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 },
+ };
+ union AES_STATE rk = { .l = {
+ float64_val(env->vfp.regs[rm]),
+ float64_val(env->vfp.regs[rm + 1])
+ } };
+ union AES_STATE st = { .l = {
+ float64_val(env->vfp.regs[rd]),
+ float64_val(env->vfp.regs[rd + 1])
+ } };
+ int i;
+
+ assert(decrypt < 2);
+
+ /* xor state vector with round key */
+ rk.l[0] ^= st.l[0];
+ rk.l[1] ^= st.l[1];
+
+ /* combine ShiftRows operation and sbox substitution */
+ for (i = 0; i < 16; i++) {
+ st.bytes[i] = sbox[decrypt][rk.bytes[shift[decrypt][i]]];
+ }
+
+ env->vfp.regs[rd] = make_float64(st.l[0]);
+ env->vfp.regs[rd + 1] = make_float64(st.l[1]);
+}
+
+void HELPER(crypto_aesmc)(CPUARMState *env, uint32_t rd, uint32_t rm,
+ uint32_t decrypt)
+{
+ static uint32_t const mc[][256] = { {
+ /* MixColumns lookup table */
+ 0x00000000, 0x03010102, 0x06020204, 0x05030306,
+ 0x0c040408, 0x0f05050a, 0x0a06060c, 0x0907070e,
+ 0x18080810, 0x1b090912, 0x1e0a0a14, 0x1d0b0b16,
+ 0x140c0c18, 0x170d0d1a, 0x120e0e1c, 0x110f0f1e,
+ 0x30101020, 0x33111122, 0x36121224, 0x35131326,
+ 0x3c141428, 0x3f15152a, 0x3a16162c, 0x3917172e,
+ 0x28181830, 0x2b191932, 0x2e1a1a34, 0x2d1b1b36,
+ 0x241c1c38, 0x271d1d3a, 0x221e1e3c, 0x211f1f3e,
+ 0x60202040, 0x63212142, 0x66222244, 0x65232346,
+ 0x6c242448, 0x6f25254a, 0x6a26264c, 0x6927274e,
+ 0x78282850, 0x7b292952, 0x7e2a2a54, 0x7d2b2b56,
+ 0x742c2c58, 0x772d2d5a, 0x722e2e5c, 0x712f2f5e,
+ 0x50303060, 0x53313162, 0x56323264, 0x55333366,
+ 0x5c343468, 0x5f35356a, 0x5a36366c, 0x5937376e,
+ 0x48383870, 0x4b393972, 0x4e3a3a74, 0x4d3b3b76,
+ 0x443c3c78, 0x473d3d7a, 0x423e3e7c, 0x413f3f7e,
+ 0xc0404080, 0xc3414182, 0xc6424284, 0xc5434386,
+ 0xcc444488, 0xcf45458a, 0xca46468c, 0xc947478e,
+ 0xd8484890, 0xdb494992, 0xde4a4a94, 0xdd4b4b96,
+ 0xd44c4c98, 0xd74d4d9a, 0xd24e4e9c, 0xd14f4f9e,
+ 0xf05050a0, 0xf35151a2, 0xf65252a4, 0xf55353a6,
+ 0xfc5454a8, 0xff5555aa, 0xfa5656ac, 0xf95757ae,
+ 0xe85858b0, 0xeb5959b2, 0xee5a5ab4, 0xed5b5bb6,
+ 0xe45c5cb8, 0xe75d5dba, 0xe25e5ebc, 0xe15f5fbe,
+ 0xa06060c0, 0xa36161c2, 0xa66262c4, 0xa56363c6,
+ 0xac6464c8, 0xaf6565ca, 0xaa6666cc, 0xa96767ce,
+ 0xb86868d0, 0xbb6969d2, 0xbe6a6ad4, 0xbd6b6bd6,
+ 0xb46c6cd8, 0xb76d6dda, 0xb26e6edc, 0xb16f6fde,
+ 0x907070e0, 0x937171e2, 0x967272e4, 0x957373e6,
+ 0x9c7474e8, 0x9f7575ea, 0x9a7676ec, 0x997777ee,
+ 0x887878f0, 0x8b7979f2, 0x8e7a7af4, 0x8d7b7bf6,
+ 0x847c7cf8, 0x877d7dfa, 0x827e7efc, 0x817f7ffe,
+ 0x9b80801b, 0x98818119, 0x9d82821f, 0x9e83831d,
+ 0x97848413, 0x94858511, 0x91868617, 0x92878715,
+ 0x8388880b, 0x80898909, 0x858a8a0f, 0x868b8b0d,
+ 0x8f8c8c03, 0x8c8d8d01, 0x898e8e07, 0x8a8f8f05,
+ 0xab90903b, 0xa8919139, 0xad92923f, 0xae93933d,
+ 0xa7949433, 0xa4959531, 0xa1969637, 0xa2979735,
+ 0xb398982b, 0xb0999929, 0xb59a9a2f, 0xb69b9b2d,
+ 0xbf9c9c23, 0xbc9d9d21, 0xb99e9e27, 0xba9f9f25,
+ 0xfba0a05b, 0xf8a1a159, 0xfda2a25f, 0xfea3a35d,
+ 0xf7a4a453, 0xf4a5a551, 0xf1a6a657, 0xf2a7a755,
+ 0xe3a8a84b, 0xe0a9a949, 0xe5aaaa4f, 0xe6abab4d,
+ 0xefacac43, 0xecadad41, 0xe9aeae47, 0xeaafaf45,
+ 0xcbb0b07b, 0xc8b1b179, 0xcdb2b27f, 0xceb3b37d,
+ 0xc7b4b473, 0xc4b5b571, 0xc1b6b677, 0xc2b7b775,
+ 0xd3b8b86b, 0xd0b9b969, 0xd5baba6f, 0xd6bbbb6d,
+ 0xdfbcbc63, 0xdcbdbd61, 0xd9bebe67, 0xdabfbf65,
+ 0x5bc0c09b, 0x58c1c199, 0x5dc2c29f, 0x5ec3c39d,
+ 0x57c4c493, 0x54c5c591, 0x51c6c697, 0x52c7c795,
+ 0x43c8c88b, 0x40c9c989, 0x45caca8f, 0x46cbcb8d,
+ 0x4fcccc83, 0x4ccdcd81, 0x49cece87, 0x4acfcf85,
+ 0x6bd0d0bb, 0x68d1d1b9, 0x6dd2d2bf, 0x6ed3d3bd,
+ 0x67d4d4b3, 0x64d5d5b1, 0x61d6d6b7, 0x62d7d7b5,
+ 0x73d8d8ab, 0x70d9d9a9, 0x75dadaaf, 0x76dbdbad,
+ 0x7fdcdca3, 0x7cdddda1, 0x79dedea7, 0x7adfdfa5,
+ 0x3be0e0db, 0x38e1e1d9, 0x3de2e2df, 0x3ee3e3dd,
+ 0x37e4e4d3, 0x34e5e5d1, 0x31e6e6d7, 0x32e7e7d5,
+ 0x23e8e8cb, 0x20e9e9c9, 0x25eaeacf, 0x26ebebcd,
+ 0x2fececc3, 0x2cededc1, 0x29eeeec7, 0x2aefefc5,
+ 0x0bf0f0fb, 0x08f1f1f9, 0x0df2f2ff, 0x0ef3f3fd,
+ 0x07f4f4f3, 0x04f5f5f1, 0x01f6f6f7, 0x02f7f7f5,
+ 0x13f8f8eb, 0x10f9f9e9, 0x15fafaef, 0x16fbfbed,
+ 0x1ffcfce3, 0x1cfdfde1, 0x19fefee7, 0x1affffe5,
+ }, {
+ /* Inverse MixColumns lookup table */
+ 0x00000000, 0x0b0d090e, 0x161a121c, 0x1d171b12,
+ 0x2c342438, 0x27392d36, 0x3a2e3624, 0x31233f2a,
+ 0x58684870, 0x5365417e, 0x4e725a6c, 0x457f5362,
+ 0x745c6c48, 0x7f516546, 0x62467e54, 0x694b775a,
+ 0xb0d090e0, 0xbbdd99ee, 0xa6ca82fc, 0xadc78bf2,
+ 0x9ce4b4d8, 0x97e9bdd6, 0x8afea6c4, 0x81f3afca,
+ 0xe8b8d890, 0xe3b5d19e, 0xfea2ca8c, 0xf5afc382,
+ 0xc48cfca8, 0xcf81f5a6, 0xd296eeb4, 0xd99be7ba,
+ 0x7bbb3bdb, 0x70b632d5, 0x6da129c7, 0x66ac20c9,
+ 0x578f1fe3, 0x5c8216ed, 0x41950dff, 0x4a9804f1,
+ 0x23d373ab, 0x28de7aa5, 0x35c961b7, 0x3ec468b9,
+ 0x0fe75793, 0x04ea5e9d, 0x19fd458f, 0x12f04c81,
+ 0xcb6bab3b, 0xc066a235, 0xdd71b927, 0xd67cb029,
+ 0xe75f8f03, 0xec52860d, 0xf1459d1f, 0xfa489411,
+ 0x9303e34b, 0x980eea45, 0x8519f157, 0x8e14f859,
+ 0xbf37c773, 0xb43ace7d, 0xa92dd56f, 0xa220dc61,
+ 0xf66d76ad, 0xfd607fa3, 0xe07764b1, 0xeb7a6dbf,
+ 0xda595295, 0xd1545b9b, 0xcc434089, 0xc74e4987,
+ 0xae053edd, 0xa50837d3, 0xb81f2cc1, 0xb31225cf,
+ 0x82311ae5, 0x893c13eb, 0x942b08f9, 0x9f2601f7,
+ 0x46bde64d, 0x4db0ef43, 0x50a7f451, 0x5baafd5f,
+ 0x6a89c275, 0x6184cb7b, 0x7c93d069, 0x779ed967,
+ 0x1ed5ae3d, 0x15d8a733, 0x08cfbc21, 0x03c2b52f,
+ 0x32e18a05, 0x39ec830b, 0x24fb9819, 0x2ff69117,
+ 0x8dd64d76, 0x86db4478, 0x9bcc5f6a, 0x90c15664,
+ 0xa1e2694e, 0xaaef6040, 0xb7f87b52, 0xbcf5725c,
+ 0xd5be0506, 0xdeb30c08, 0xc3a4171a, 0xc8a91e14,
+ 0xf98a213e, 0xf2872830, 0xef903322, 0xe49d3a2c,
+ 0x3d06dd96, 0x360bd498, 0x2b1ccf8a, 0x2011c684,
+ 0x1132f9ae, 0x1a3ff0a0, 0x0728ebb2, 0x0c25e2bc,
+ 0x656e95e6, 0x6e639ce8, 0x737487fa, 0x78798ef4,
+ 0x495ab1de, 0x4257b8d0, 0x5f40a3c2, 0x544daacc,
+ 0xf7daec41, 0xfcd7e54f, 0xe1c0fe5d, 0xeacdf753,
+ 0xdbeec879, 0xd0e3c177, 0xcdf4da65, 0xc6f9d36b,
+ 0xafb2a431, 0xa4bfad3f, 0xb9a8b62d, 0xb2a5bf23,
+ 0x83868009, 0x888b8907, 0x959c9215, 0x9e919b1b,
+ 0x470a7ca1, 0x4c0775af, 0x51106ebd, 0x5a1d67b3,
+ 0x6b3e5899, 0x60335197, 0x7d244a85, 0x7629438b,
+ 0x1f6234d1, 0x146f3ddf, 0x097826cd, 0x02752fc3,
+ 0x335610e9, 0x385b19e7, 0x254c02f5, 0x2e410bfb,
+ 0x8c61d79a, 0x876cde94, 0x9a7bc586, 0x9176cc88,
+ 0xa055f3a2, 0xab58faac, 0xb64fe1be, 0xbd42e8b0,
+ 0xd4099fea, 0xdf0496e4, 0xc2138df6, 0xc91e84f8,
+ 0xf83dbbd2, 0xf330b2dc, 0xee27a9ce, 0xe52aa0c0,
+ 0x3cb1477a, 0x37bc4e74, 0x2aab5566, 0x21a65c68,
+ 0x10856342, 0x1b886a4c, 0x069f715e, 0x0d927850,
+ 0x64d90f0a, 0x6fd40604, 0x72c31d16, 0x79ce1418,
+ 0x48ed2b32, 0x43e0223c, 0x5ef7392e, 0x55fa3020,
+ 0x01b79aec, 0x0aba93e2, 0x17ad88f0, 0x1ca081fe,
+ 0x2d83bed4, 0x268eb7da, 0x3b99acc8, 0x3094a5c6,
+ 0x59dfd29c, 0x52d2db92, 0x4fc5c080, 0x44c8c98e,
+ 0x75ebf6a4, 0x7ee6ffaa, 0x63f1e4b8, 0x68fcedb6,
+ 0xb1670a0c, 0xba6a0302, 0xa77d1810, 0xac70111e,
+ 0x9d532e34, 0x965e273a, 0x8b493c28, 0x80443526,
+ 0xe90f427c, 0xe2024b72, 0xff155060, 0xf418596e,
+ 0xc53b6644, 0xce366f4a, 0xd3217458, 0xd82c7d56,
+ 0x7a0ca137, 0x7101a839, 0x6c16b32b, 0x671bba25,
+ 0x5638850f, 0x5d358c01, 0x40229713, 0x4b2f9e1d,
+ 0x2264e947, 0x2969e049, 0x347efb5b, 0x3f73f255,
+ 0x0e50cd7f, 0x055dc471, 0x184adf63, 0x1347d66d,
+ 0xcadc31d7, 0xc1d138d9, 0xdcc623cb, 0xd7cb2ac5,
+ 0xe6e815ef, 0xede51ce1, 0xf0f207f3, 0xfbff0efd,
+ 0x92b479a7, 0x99b970a9, 0x84ae6bbb, 0x8fa362b5,
+ 0xbe805d9f, 0xb58d5491, 0xa89a4f83, 0xa397468d,
+ } };
+ union AES_STATE st = { .l = {
+ float64_val(env->vfp.regs[rm]),
+ float64_val(env->vfp.regs[rm + 1])
+ } };
+ int i;
+
+ assert(decrypt < 2);
+
+ for (i = 0; i < 16; i += 4) {
+ st.cols[i >> 2] = cpu_to_le32(
+ mc[decrypt][st.bytes[i]] ^
+ rol32(mc[decrypt][st.bytes[i + 1]], 8) ^
+ rol32(mc[decrypt][st.bytes[i + 2]], 16) ^
+ rol32(mc[decrypt][st.bytes[i + 3]], 24));
+ }
+
+ env->vfp.regs[rd] = make_float64(st.l[0]);
+ env->vfp.regs[rd + 1] = make_float64(st.l[1]);
+}
diff --git a/target-arm/gdbstub64.c b/target-arm/gdbstub64.c
index 7cb6a7c0e0..e8a82952a4 100644
--- a/target-arm/gdbstub64.c
+++ b/target-arm/gdbstub64.c
@@ -37,7 +37,7 @@ int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
return gdb_get_reg64(mem_buf, env->pc);
break;
case 33:
- return gdb_get_reg32(mem_buf, env->pstate);
+ return gdb_get_reg32(mem_buf, pstate_read(env));
}
/* Unknown register. */
return 0;
@@ -65,7 +65,7 @@ int aarch64_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
return 8;
case 33:
/* CPSR */
- env->pstate = tmp;
+ pstate_write(env, tmp);
return 4;
}
/* Unknown register. */
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
new file mode 100644
index 0000000000..4ce0d01a85
--- /dev/null
+++ b/target-arm/helper-a64.c
@@ -0,0 +1,124 @@
+/*
+ * AArch64 specific helpers
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cpu.h"
+#include "exec/gdbstub.h"
+#include "helper.h"
+#include "qemu/host-utils.h"
+#include "sysemu/sysemu.h"
+#include "qemu/bitops.h"
+
+/* C2.4.7 Multiply and divide */
+/* special cases for 0 and LLONG_MIN are mandated by the standard */
+uint64_t HELPER(udiv64)(uint64_t num, uint64_t den)
+{
+ if (den == 0) {
+ return 0;
+ }
+ return num / den;
+}
+
+int64_t HELPER(sdiv64)(int64_t num, int64_t den)
+{
+ if (den == 0) {
+ return 0;
+ }
+ if (num == LLONG_MIN && den == -1) {
+ return LLONG_MIN;
+ }
+ return num / den;
+}
+
+uint64_t HELPER(clz64)(uint64_t x)
+{
+ return clz64(x);
+}
+
+uint64_t HELPER(cls64)(uint64_t x)
+{
+ return clrsb64(x);
+}
+
+uint32_t HELPER(cls32)(uint32_t x)
+{
+ return clrsb32(x);
+}
+
+uint64_t HELPER(rbit64)(uint64_t x)
+{
+ /* assign the correct byte position */
+ x = bswap64(x);
+
+ /* assign the correct nibble position */
+ x = ((x & 0xf0f0f0f0f0f0f0f0ULL) >> 4)
+ | ((x & 0x0f0f0f0f0f0f0f0fULL) << 4);
+
+ /* assign the correct bit position */
+ x = ((x & 0x8888888888888888ULL) >> 3)
+ | ((x & 0x4444444444444444ULL) >> 1)
+ | ((x & 0x2222222222222222ULL) << 1)
+ | ((x & 0x1111111111111111ULL) << 3);
+
+ return x;
+}
+
+/* Convert a softfloat float_relation_ (as returned by
+ * the float*_compare functions) to the correct ARM
+ * NZCV flag state.
+ */
+static inline uint32_t float_rel_to_flags(int res)
+{
+ uint64_t flags;
+ switch (res) {
+ case float_relation_equal:
+ flags = PSTATE_Z | PSTATE_C;
+ break;
+ case float_relation_less:
+ flags = PSTATE_N;
+ break;
+ case float_relation_greater:
+ flags = PSTATE_C;
+ break;
+ case float_relation_unordered:
+ default:
+ flags = PSTATE_C | PSTATE_V;
+ break;
+ }
+ return flags;
+}
+
+uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status)
+{
+ return float_rel_to_flags(float32_compare_quiet(x, y, fp_status));
+}
+
+uint64_t HELPER(vfp_cmpes_a64)(float32 x, float32 y, void *fp_status)
+{
+ return float_rel_to_flags(float32_compare(x, y, fp_status));
+}
+
+uint64_t HELPER(vfp_cmpd_a64)(float64 x, float64 y, void *fp_status)
+{
+ return float_rel_to_flags(float64_compare_quiet(x, y, fp_status));
+}
+
+uint64_t HELPER(vfp_cmped_a64)(float64 x, float64 y, void *fp_status)
+{
+ return float_rel_to_flags(float64_compare(x, y, fp_status));
+}
diff --git a/target-arm/helper-a64.h b/target-arm/helper-a64.h
new file mode 100644
index 0000000000..bca19f3dea
--- /dev/null
+++ b/target-arm/helper-a64.h
@@ -0,0 +1,28 @@
+/*
+ * AArch64 specific helper definitions
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
+ *
+ * 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/>.
+ */
+DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
+DEF_HELPER_FLAGS_1(clz64, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(cls64, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_FLAGS_1(cls32, TCG_CALL_NO_RWG_SE, i32, i32)
+DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64)
+DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr)
+DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr)
+DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr)
+DEF_HELPER_3(vfp_cmped_a64, i64, f64, f64, ptr)
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 5e5e5aad2b..c708f15e27 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -65,6 +65,48 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
return 0;
}
+static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
+{
+ switch (reg) {
+ case 0 ... 31:
+ /* 128 bit FP register */
+ stfq_le_p(buf, env->vfp.regs[reg * 2]);
+ stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]);
+ return 16;
+ case 32:
+ /* FPSR */
+ stl_p(buf, vfp_get_fpsr(env));
+ return 4;
+ case 33:
+ /* FPCR */
+ stl_p(buf, vfp_get_fpcr(env));
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
+{
+ switch (reg) {
+ case 0 ... 31:
+ /* 128 bit FP register */
+ env->vfp.regs[reg * 2] = ldfq_le_p(buf);
+ env->vfp.regs[reg * 2 + 1] = ldfq_le_p(buf + 8);
+ return 16;
+ case 32:
+ /* FPSR */
+ vfp_set_fpsr(env, ldl_p(buf));
+ return 4;
+ case 33:
+ /* FPCR */
+ vfp_set_fpcr(env, ldl_p(buf));
+ return 4;
+ default:
+ return 0;
+ }
+}
+
static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t *value)
{
@@ -100,11 +142,7 @@ static bool read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri,
} else if (ri->readfn) {
return (ri->readfn(env, ri, v) == 0);
} else {
- if (ri->type & ARM_CP_64BIT) {
- *v = CPREG_FIELD64(env, ri);
- } else {
- *v = CPREG_FIELD32(env, ri);
- }
+ raw_read(env, ri, v);
}
return true;
}
@@ -125,11 +163,7 @@ static bool write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri,
} else if (ri->writefn) {
return (ri->writefn(env, ri, v) == 0);
} else {
- if (ri->type & ARM_CP_64BIT) {
- CPREG_FIELD64(env, ri) = v;
- } else {
- CPREG_FIELD32(env, ri) = v;
- }
+ raw_write(env, ri, v);
}
return true;
}
@@ -144,7 +178,7 @@ bool write_cpustate_to_list(ARMCPU *cpu)
uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
const ARMCPRegInfo *ri;
uint64_t v;
- ri = get_arm_cp_reginfo(cpu, regidx);
+ ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
if (!ri) {
ok = false;
continue;
@@ -172,7 +206,7 @@ bool write_list_to_cpustate(ARMCPU *cpu)
uint64_t readback;
const ARMCPRegInfo *ri;
- ri = get_arm_cp_reginfo(cpu, regidx);
+ ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
if (!ri) {
ok = false;
continue;
@@ -200,7 +234,7 @@ static void add_cpreg_to_list(gpointer key, gpointer opaque)
const ARMCPRegInfo *ri;
regidx = *(uint32_t *)key;
- ri = get_arm_cp_reginfo(cpu, regidx);
+ ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
if (!(ri->type & ARM_CP_NO_MIGRATE)) {
cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx);
@@ -216,7 +250,7 @@ static void count_cpreg(gpointer key, gpointer opaque)
const ARMCPRegInfo *ri;
regidx = *(uint32_t *)key;
- ri = get_arm_cp_reginfo(cpu, regidx);
+ ri = get_arm_cp_reginfo(cpu->cp_regs, regidx);
if (!(ri->type & ARM_CP_NO_MIGRATE)) {
cpu->cpreg_array_len++;
@@ -355,7 +389,7 @@ static const ARMCPRegInfo cp_reginfo[] = {
.access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse),
.resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, },
{ .name = "CONTEXTIDR", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 1,
- .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_fcse),
+ .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c13_context),
.resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, },
/* ??? This covers not just the impdef TLB lockdown registers but also
* some v7VMSA registers relating to TEX remap, so it is overly broad.
@@ -698,18 +732,26 @@ static const ARMCPRegInfo t2ee_cp_reginfo[] = {
};
static const ARMCPRegInfo v6k_cp_reginfo[] = {
+ { .name = "TPIDR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 2, .crn = 13, .crm = 0,
+ .access = PL0_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el0), .resetvalue = 0 },
{ .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL0_RW,
- .fieldoffset = offsetof(CPUARMState, cp15.c13_tls1),
- .resetvalue = 0 },
+ .fieldoffset = offsetoflow32(CPUARMState, cp15.tpidr_el0),
+ .resetfn = arm_cp_reset_ignore },
+ { .name = "TPIDRRO_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 3, .crn = 13, .crm = 0,
+ .access = PL0_R|PL1_W,
+ .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el0), .resetvalue = 0 },
{ .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3,
.access = PL0_R|PL1_W,
- .fieldoffset = offsetof(CPUARMState, cp15.c13_tls2),
- .resetvalue = 0 },
- { .name = "TPIDRPRW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 4,
+ .fieldoffset = offsetoflow32(CPUARMState, cp15.tpidrro_el0),
+ .resetfn = arm_cp_reset_ignore },
+ { .name = "TPIDR_EL1", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 0, .opc2 = 4, .crn = 13, .crm = 0,
.access = PL1_RW,
- .fieldoffset = offsetof(CPUARMState, cp15.c13_tls3),
- .resetvalue = 0 },
+ .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el1), .resetvalue = 0 },
REGINFO_SENTINEL
};
@@ -1338,7 +1380,8 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = {
*/
{ .name = "C15_IMPDEF", .cp = 15, .crn = 15,
.crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
- .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE,
+ .access = PL1_RW,
+ .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE,
.resetvalue = 0 },
REGINFO_SENTINEL
};
@@ -1517,6 +1560,64 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
REGINFO_SENTINEL
};
+static int aa64_fpcr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ *value = vfp_get_fpcr(env);
+ return 0;
+}
+
+static int aa64_fpcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ vfp_set_fpcr(env, value);
+ return 0;
+}
+
+static int aa64_fpsr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t *value)
+{
+ *value = vfp_get_fpsr(env);
+ return 0;
+}
+
+static int aa64_fpsr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ vfp_set_fpsr(env, value);
+ return 0;
+}
+
+static const ARMCPRegInfo v8_cp_reginfo[] = {
+ /* Minimal set of EL0-visible registers. This will need to be expanded
+ * significantly for system emulation of AArch64 CPUs.
+ */
+ { .name = "NZCV", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 2,
+ .access = PL0_RW, .type = ARM_CP_NZCV },
+ { .name = "FPCR", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 0, .crn = 4, .crm = 4,
+ .access = PL0_RW, .readfn = aa64_fpcr_read, .writefn = aa64_fpcr_write },
+ { .name = "FPSR", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 4,
+ .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write },
+ /* This claims a 32 byte cacheline size for icache and dcache, VIPT icache.
+ * It will eventually need to have a CPU-specified reset value.
+ */
+ { .name = "CTR_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 0, .crm = 0,
+ .access = PL0_R, .type = ARM_CP_CONST,
+ .resetvalue = 0x80030003 },
+ /* Prohibit use of DC ZVA. OPTME: implement DC ZVA and allow its use.
+ * For system mode the DZP bit here will need to be computed, not constant.
+ */
+ { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0,
+ .access = PL0_R, .type = ARM_CP_CONST,
+ .resetvalue = 0x10 },
+ REGINFO_SENTINEL
+};
+
static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
{
env->cp15.c1_sys = value;
@@ -1619,6 +1720,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
} else {
define_arm_cp_regs(cpu, not_v7_cp_reginfo);
}
+ if (arm_feature(env, ARM_FEATURE_V8)) {
+ define_arm_cp_regs(cpu, v8_cp_reginfo);
+ }
if (arm_feature(env, ARM_FEATURE_MPU)) {
/* These are the MPU registers prior to PMSAv6. Any new
* PMSA core later than the ARM946 will require that we
@@ -1744,6 +1848,15 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_one_arm_cp_reg(cpu, &auxcr);
}
+ if (arm_feature(env, ARM_FEATURE_CBAR)) {
+ ARMCPRegInfo cbar = {
+ .name = "CBAR", .cp = 15, .crn = 15, .crm = 0, .opc1 = 4, .opc2 = 0,
+ .access = PL1_R|PL3_W, .resetvalue = cpu->reset_cbar,
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_config_base_address)
+ };
+ define_one_arm_cp_reg(cpu, &cbar);
+ }
+
/* Generic registers whose values depend on the implementation */
{
ARMCPRegInfo sctlr = {
@@ -1785,7 +1898,11 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
- if (arm_feature(env, ARM_FEATURE_NEON)) {
+ if (arm_feature(env, ARM_FEATURE_AARCH64)) {
+ gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg,
+ aarch64_fpu_gdb_set_reg,
+ 34, "aarch64-fpu.xml", 0);
+ } else if (arm_feature(env, ARM_FEATURE_NEON)) {
gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg,
51, "arm-neon.xml", 0);
} else if (arm_feature(env, ARM_FEATURE_VFP3)) {
@@ -1881,6 +1998,85 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
return cpu_list;
}
+static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
+ void *opaque, int state,
+ int crm, int opc1, int opc2)
+{
+ /* Private utility function for define_one_arm_cp_reg_with_opaque():
+ * add a single reginfo struct to the hash table.
+ */
+ uint32_t *key = g_new(uint32_t, 1);
+ ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo));
+ int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0;
+ if (r->state == ARM_CP_STATE_BOTH && state == ARM_CP_STATE_AA32) {
+ /* The AArch32 view of a shared register sees the lower 32 bits
+ * of a 64 bit backing field. It is not migratable as the AArch64
+ * view handles that. AArch64 also handles reset.
+ * We assume it is a cp15 register.
+ */
+ r2->cp = 15;
+ r2->type |= ARM_CP_NO_MIGRATE;
+ r2->resetfn = arm_cp_reset_ignore;
+#ifdef HOST_WORDS_BIGENDIAN
+ if (r2->fieldoffset) {
+ r2->fieldoffset += sizeof(uint32_t);
+ }
+#endif
+ }
+ if (state == ARM_CP_STATE_AA64) {
+ /* To allow abbreviation of ARMCPRegInfo
+ * definitions, we treat cp == 0 as equivalent to
+ * the value for "standard guest-visible sysreg".
+ */
+ if (r->cp == 0) {
+ r2->cp = CP_REG_ARM64_SYSREG_CP;
+ }
+ *key = ENCODE_AA64_CP_REG(r2->cp, r2->crn, crm,
+ r2->opc0, opc1, opc2);
+ } else {
+ *key = ENCODE_CP_REG(r2->cp, is64, r2->crn, crm, opc1, opc2);
+ }
+ if (opaque) {
+ r2->opaque = opaque;
+ }
+ /* Make sure reginfo passed to helpers for wildcarded regs
+ * has the correct crm/opc1/opc2 for this reg, not CP_ANY:
+ */
+ r2->crm = crm;
+ r2->opc1 = opc1;
+ r2->opc2 = opc2;
+ /* By convention, for wildcarded registers only the first
+ * entry is used for migration; the others are marked as
+ * NO_MIGRATE so we don't try to transfer the register
+ * multiple times. Special registers (ie NOP/WFI) are
+ * never migratable.
+ */
+ if ((r->type & ARM_CP_SPECIAL) ||
+ ((r->crm == CP_ANY) && crm != 0) ||
+ ((r->opc1 == CP_ANY) && opc1 != 0) ||
+ ((r->opc2 == CP_ANY) && opc2 != 0)) {
+ r2->type |= ARM_CP_NO_MIGRATE;
+ }
+
+ /* Overriding of an existing definition must be explicitly
+ * requested.
+ */
+ if (!(r->type & ARM_CP_OVERRIDE)) {
+ ARMCPRegInfo *oldreg;
+ oldreg = g_hash_table_lookup(cpu->cp_regs, key);
+ if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) {
+ fprintf(stderr, "Register redefined: cp=%d %d bit "
+ "crn=%d crm=%d opc1=%d opc2=%d, "
+ "was %s, now %s\n", r2->cp, 32 + 32 * is64,
+ r2->crn, r2->crm, r2->opc1, r2->opc2,
+ oldreg->name, r2->name);
+ g_assert_not_reached();
+ }
+ }
+ g_hash_table_insert(cpu->cp_regs, key, r2);
+}
+
+
void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
const ARMCPRegInfo *r, void *opaque)
{
@@ -1895,8 +2091,19 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
* At least one of the original and the second definition should
* include ARM_CP_OVERRIDE in its type bits -- this is just a guard
* against accidental use.
+ *
+ * The state field defines whether the register is to be
+ * visible in the AArch32 or AArch64 execution state. If the
+ * state is set to ARM_CP_STATE_BOTH then we synthesise a
+ * reginfo structure for the AArch32 view, which sees the lower
+ * 32 bits of the 64 bit register.
+ *
+ * Only registers visible in AArch64 may set r->opc0; opc0 cannot
+ * be wildcarded. AArch64 registers are always considered to be 64
+ * bits; the ARM_CP_64BIT* flag applies only to the AArch32 view of
+ * the register, if any.
*/
- int crm, opc1, opc2;
+ int crm, opc1, opc2, state;
int crmmin = (r->crm == CP_ANY) ? 0 : r->crm;
int crmmax = (r->crm == CP_ANY) ? 15 : r->crm;
int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1;
@@ -1905,6 +2112,52 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2;
/* 64 bit registers have only CRm and Opc1 fields */
assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn)));
+ /* op0 only exists in the AArch64 encodings */
+ assert((r->state != ARM_CP_STATE_AA32) || (r->opc0 == 0));
+ /* AArch64 regs are all 64 bit so ARM_CP_64BIT is meaningless */
+ assert((r->state != ARM_CP_STATE_AA64) || !(r->type & ARM_CP_64BIT));
+ /* The AArch64 pseudocode CheckSystemAccess() specifies that op1
+ * encodes a minimum access level for the register. We roll this
+ * runtime check into our general permission check code, so check
+ * here that the reginfo's specified permissions are strict enough
+ * to encompass the generic architectural permission check.
+ */
+ if (r->state != ARM_CP_STATE_AA32) {
+ int mask = 0;
+ switch (r->opc1) {
+ case 0: case 1: case 2:
+ /* min_EL EL1 */
+ mask = PL1_RW;
+ break;
+ case 3:
+ /* min_EL EL0 */
+ mask = PL0_RW;
+ break;
+ case 4:
+ /* min_EL EL2 */
+ mask = PL2_RW;
+ break;
+ case 5:
+ /* unallocated encoding, so not possible */
+ assert(false);
+ break;
+ case 6:
+ /* min_EL EL3 */
+ mask = PL3_RW;
+ break;
+ case 7:
+ /* min_EL EL1, secure mode only (we don't check the latter) */
+ mask = PL1_RW;
+ break;
+ default:
+ /* broken reginfo with out-of-range opc1 */
+ assert(false);
+ break;
+ }
+ /* assert our permissions are not too lax (stricter is fine) */
+ assert((r->access & ~mask) == 0);
+ }
+
/* Check that the register definition has enough info to handle
* reads and writes if they are permitted.
*/
@@ -1921,48 +2174,14 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
for (crm = crmmin; crm <= crmmax; crm++) {
for (opc1 = opc1min; opc1 <= opc1max; opc1++) {
for (opc2 = opc2min; opc2 <= opc2max; opc2++) {
- uint32_t *key = g_new(uint32_t, 1);
- ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo));
- int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0;
- *key = ENCODE_CP_REG(r->cp, is64, r->crn, crm, opc1, opc2);
- if (opaque) {
- r2->opaque = opaque;
- }
- /* Make sure reginfo passed to helpers for wildcarded regs
- * has the correct crm/opc1/opc2 for this reg, not CP_ANY:
- */
- r2->crm = crm;
- r2->opc1 = opc1;
- r2->opc2 = opc2;
- /* By convention, for wildcarded registers only the first
- * entry is used for migration; the others are marked as
- * NO_MIGRATE so we don't try to transfer the register
- * multiple times. Special registers (ie NOP/WFI) are
- * never migratable.
- */
- if ((r->type & ARM_CP_SPECIAL) ||
- ((r->crm == CP_ANY) && crm != 0) ||
- ((r->opc1 == CP_ANY) && opc1 != 0) ||
- ((r->opc2 == CP_ANY) && opc2 != 0)) {
- r2->type |= ARM_CP_NO_MIGRATE;
- }
-
- /* Overriding of an existing definition must be explicitly
- * requested.
- */
- if (!(r->type & ARM_CP_OVERRIDE)) {
- ARMCPRegInfo *oldreg;
- oldreg = g_hash_table_lookup(cpu->cp_regs, key);
- if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) {
- fprintf(stderr, "Register redefined: cp=%d %d bit "
- "crn=%d crm=%d opc1=%d opc2=%d, "
- "was %s, now %s\n", r2->cp, 32 + 32 * is64,
- r2->crn, r2->crm, r2->opc1, r2->opc2,
- oldreg->name, r2->name);
- g_assert_not_reached();
+ for (state = ARM_CP_STATE_AA32;
+ state <= ARM_CP_STATE_AA64; state++) {
+ if (r->state != state && r->state != ARM_CP_STATE_BOTH) {
+ continue;
}
+ add_cpreg_to_hashtable(cpu, r, opaque, state,
+ crm, opc1, opc2);
}
- g_hash_table_insert(cpu->cp_regs, key, r2);
}
}
}
@@ -1978,9 +2197,9 @@ void define_arm_cp_regs_with_opaque(ARMCPU *cpu,
}
}
-const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp)
+const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp)
{
- return g_hash_table_lookup(cpu->cp_regs, &encoded_cp);
+ return g_hash_table_lookup(cpregs, &encoded_cp);
}
int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -1997,6 +2216,11 @@ int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value)
return 0;
}
+void arm_cp_reset_ignore(CPUARMState *env, const ARMCPRegInfo *opaque)
+{
+ /* Helper coprocessor reset function for do-nothing-on-reset registers */
+}
+
static int bad_mode_switch(CPUARMState *env, int mode)
{
/* Return true if it is not valid for us to switch to
@@ -3583,16 +3807,16 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val)
if (changed & (3 << 22)) {
i = (val >> 22) & 3;
switch (i) {
- case 0:
+ case FPROUNDING_TIEEVEN:
i = float_round_nearest_even;
break;
- case 1:
+ case FPROUNDING_POSINF:
i = float_round_up;
break;
- case 2:
+ case FPROUNDING_NEGINF:
i = float_round_down;
break;
- case 3:
+ case FPROUNDING_ZERO:
i = float_round_to_zero;
break;
}
@@ -3632,6 +3856,10 @@ VFP_BINOP(add)
VFP_BINOP(sub)
VFP_BINOP(mul)
VFP_BINOP(div)
+VFP_BINOP(min)
+VFP_BINOP(max)
+VFP_BINOP(minnum)
+VFP_BINOP(maxnum)
#undef VFP_BINOP
float32 VFP_HELPER(neg, s)(float32 a)
@@ -3748,37 +3976,77 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env)
}
/* VFP3 fixed point conversion. */
-#define VFP_CONV_FIX(name, p, fsz, itype, sign) \
-float##fsz HELPER(vfp_##name##to##p)(uint##fsz##_t x, uint32_t shift, \
- void *fpstp) \
+#define VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \
+float##fsz HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \
+ void *fpstp) \
{ \
float_status *fpst = fpstp; \
float##fsz tmp; \
- tmp = sign##int32_to_##float##fsz((itype##_t)x, fpst); \
+ tmp = itype##_to_##float##fsz(x, fpst); \
return float##fsz##_scalbn(tmp, -(int)shift, fpst); \
-} \
-uint##fsz##_t HELPER(vfp_to##name##p)(float##fsz x, uint32_t shift, \
- void *fpstp) \
+}
+
+/* Notice that we want only input-denormal exception flags from the
+ * scalbn operation: the other possible flags (overflow+inexact if
+ * we overflow to infinity, output-denormal) aren't correct for the
+ * complete scale-and-convert operation.
+ */
+#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, round) \
+uint##isz##_t HELPER(vfp_to##name##p##round)(float##fsz x, \
+ uint32_t shift, \
+ void *fpstp) \
{ \
float_status *fpst = fpstp; \
+ int old_exc_flags = get_float_exception_flags(fpst); \
float##fsz tmp; \
if (float##fsz##_is_any_nan(x)) { \
float_raise(float_flag_invalid, fpst); \
return 0; \
} \
tmp = float##fsz##_scalbn(x, shift, fpst); \
- return float##fsz##_to_##itype##_round_to_zero(tmp, fpst); \
-}
-
-VFP_CONV_FIX(sh, d, 64, int16, )
-VFP_CONV_FIX(sl, d, 64, int32, )
-VFP_CONV_FIX(uh, d, 64, uint16, u)
-VFP_CONV_FIX(ul, d, 64, uint32, u)
-VFP_CONV_FIX(sh, s, 32, int16, )
-VFP_CONV_FIX(sl, s, 32, int32, )
-VFP_CONV_FIX(uh, s, 32, uint16, u)
-VFP_CONV_FIX(ul, s, 32, uint32, u)
+ old_exc_flags |= get_float_exception_flags(fpst) \
+ & float_flag_input_denormal; \
+ set_float_exception_flags(old_exc_flags, fpst); \
+ return float##fsz##_to_##itype##round(tmp, fpst); \
+}
+
+#define VFP_CONV_FIX(name, p, fsz, isz, itype) \
+VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \
+VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, _round_to_zero) \
+VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, )
+
+#define VFP_CONV_FIX_A64(name, p, fsz, isz, itype) \
+VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \
+VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, )
+
+VFP_CONV_FIX(sh, d, 64, 64, int16)
+VFP_CONV_FIX(sl, d, 64, 64, int32)
+VFP_CONV_FIX_A64(sq, d, 64, 64, int64)
+VFP_CONV_FIX(uh, d, 64, 64, uint16)
+VFP_CONV_FIX(ul, d, 64, 64, uint32)
+VFP_CONV_FIX_A64(uq, d, 64, 64, uint64)
+VFP_CONV_FIX(sh, s, 32, 32, int16)
+VFP_CONV_FIX(sl, s, 32, 32, int32)
+VFP_CONV_FIX_A64(sq, s, 32, 64, int64)
+VFP_CONV_FIX(uh, s, 32, 32, uint16)
+VFP_CONV_FIX(ul, s, 32, 32, uint32)
+VFP_CONV_FIX_A64(uq, s, 32, 64, uint64)
#undef VFP_CONV_FIX
+#undef VFP_CONV_FIX_FLOAT
+#undef VFP_CONV_FLOAT_FIX_ROUND
+
+/* Set the current fp rounding mode and return the old one.
+ * The argument is a softfloat float_round_ value.
+ */
+uint32_t HELPER(set_rmode)(uint32_t rmode, CPUARMState *env)
+{
+ float_status *fp_status = &env->vfp.fp_status;
+
+ uint32_t prev_rmode = get_float_rounding_mode(fp_status);
+ set_float_rounding_mode(rmode, fp_status);
+
+ return prev_rmode;
+}
/* Half precision conversions. */
static float32 do_fcvt_f16_to_f32(uint32_t a, CPUARMState *env, float_status *s)
@@ -3821,6 +4089,26 @@ uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUARMState *env)
return do_fcvt_f32_to_f16(a, env, &env->vfp.fp_status);
}
+float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, CPUARMState *env)
+{
+ int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0;
+ float64 r = float16_to_float64(make_float16(a), ieee, &env->vfp.fp_status);
+ if (ieee) {
+ return float64_maybe_silence_nan(r);
+ }
+ return r;
+}
+
+uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, CPUARMState *env)
+{
+ int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0;
+ float16 r = float64_to_float16(a, ieee, &env->vfp.fp_status);
+ if (ieee) {
+ r = float16_maybe_silence_nan(r);
+ }
+ return float16_val(r);
+}
+
#define float32_two make_float32(0x40000000)
#define float32_three make_float32(0x40400000)
#define float32_one_point_five make_float32(0x3fc00000)
@@ -4086,27 +4374,47 @@ float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp)
return float64_muladd(a, b, c, 0, fpst);
}
-/* ARMv8 VMAXNM/VMINNM */
-float32 VFP_HELPER(maxnm, s)(float32 a, float32 b, void *fpstp)
+/* ARMv8 round to integral */
+float32 HELPER(rints_exact)(float32 x, void *fp_status)
{
- float_status *fpst = fpstp;
- return float32_maxnum(a, b, fpst);
+ return float32_round_to_int(x, fp_status);
}
-float64 VFP_HELPER(maxnm, d)(float64 a, float64 b, void *fpstp)
+float64 HELPER(rintd_exact)(float64 x, void *fp_status)
{
- float_status *fpst = fpstp;
- return float64_maxnum(a, b, fpst);
+ return float64_round_to_int(x, fp_status);
}
-float32 VFP_HELPER(minnm, s)(float32 a, float32 b, void *fpstp)
+float32 HELPER(rints)(float32 x, void *fp_status)
{
- float_status *fpst = fpstp;
- return float32_minnum(a, b, fpst);
+ int old_flags = get_float_exception_flags(fp_status), new_flags;
+ float32 ret;
+
+ ret = float32_round_to_int(x, fp_status);
+
+ /* Suppress any inexact exceptions the conversion produced */
+ if (!(old_flags & float_flag_inexact)) {
+ new_flags = get_float_exception_flags(fp_status);
+ set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status);
+ }
+
+ return ret;
}
-float64 VFP_HELPER(minnm, d)(float64 a, float64 b, void *fpstp)
+float64 HELPER(rintd)(float64 x, void *fp_status)
{
- float_status *fpst = fpstp;
- return float64_minnum(a, b, fpst);
+ int old_flags = get_float_exception_flags(fp_status), new_flags;
+ float64 ret;
+
+ ret = float64_round_to_int(x, fp_status);
+
+ new_flags = get_float_exception_flags(fp_status);
+
+ /* Suppress any inexact exceptions the conversion produced */
+ if (!(old_flags & float_flag_inexact)) {
+ new_flags = get_float_exception_flags(fp_status);
+ set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status);
+ }
+
+ return ret;
}
diff --git a/target-arm/helper.h b/target-arm/helper.h
index d459a39e46..70872dffc6 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -79,6 +79,14 @@ DEF_HELPER_3(vfp_muls, f32, f32, f32, ptr)
DEF_HELPER_3(vfp_muld, f64, f64, f64, ptr)
DEF_HELPER_3(vfp_divs, f32, f32, f32, ptr)
DEF_HELPER_3(vfp_divd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_maxs, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_maxd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_mins, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_mind, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_maxnums, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr)
+DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr)
+DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr)
DEF_HELPER_1(vfp_negs, f32, f32)
DEF_HELPER_1(vfp_negd, f64, f64)
DEF_HELPER_1(vfp_abss, f32, f32)
@@ -107,36 +115,51 @@ DEF_HELPER_2(vfp_tosid, i32, f64, ptr)
DEF_HELPER_2(vfp_tosizs, i32, f32, ptr)
DEF_HELPER_2(vfp_tosizd, i32, f64, ptr)
+DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr)
DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr)
DEF_HELPER_3(vfp_touhs, i32, f32, i32, ptr)
DEF_HELPER_3(vfp_touls, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_touqs, i64, f32, i32, ptr)
DEF_HELPER_3(vfp_toshd, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_tosld, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_tosqd, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_touhd, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_tould, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_touqd, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_shtos, f32, i32, i32, ptr)
DEF_HELPER_3(vfp_sltos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_sqtos, f32, i64, i32, ptr)
DEF_HELPER_3(vfp_uhtos, f32, i32, i32, ptr)
DEF_HELPER_3(vfp_ultos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_uqtos, f32, i64, i32, ptr)
DEF_HELPER_3(vfp_shtod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_sltod, f64, i64, i32, ptr)
+DEF_HELPER_3(vfp_sqtod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr)
+DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr)
+
+DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, env)
DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env)
DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env)
DEF_HELPER_2(neon_fcvt_f16_to_f32, f32, i32, env)
DEF_HELPER_2(neon_fcvt_f32_to_f16, i32, f32, env)
+DEF_HELPER_FLAGS_2(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, i32, env)
+DEF_HELPER_FLAGS_2(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, i32, f64, env)
DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr)
DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr)
-DEF_HELPER_3(vfp_maxnmd, f64, f64, f64, ptr)
-DEF_HELPER_3(vfp_maxnms, f32, f32, f32, ptr)
-DEF_HELPER_3(vfp_minnmd, f64, f64, f64, ptr)
-DEF_HELPER_3(vfp_minnms, f32, f32, f32, ptr)
-
DEF_HELPER_3(recps_f32, f32, f32, f32, env)
DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env)
DEF_HELPER_2(recpe_f32, f32, f32, env)
@@ -150,6 +173,11 @@ DEF_HELPER_3(shr_cc, i32, env, i32, i32)
DEF_HELPER_3(sar_cc, i32, env, i32, i32)
DEF_HELPER_3(ror_cc, i32, env, i32, i32)
+DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, ptr)
+DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, ptr)
+DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, ptr)
+DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr)
+
/* neon_helper.c */
DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32)
DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32)
@@ -346,8 +374,6 @@ DEF_HELPER_2(neon_qneg_s8, i32, env, i32)
DEF_HELPER_2(neon_qneg_s16, i32, env, i32)
DEF_HELPER_2(neon_qneg_s32, i32, env, i32)
-DEF_HELPER_3(neon_min_f32, i32, i32, i32, ptr)
-DEF_HELPER_3(neon_max_f32, i32, i32, i32, ptr)
DEF_HELPER_3(neon_abd_f32, i32, i32, i32, ptr)
DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, ptr)
DEF_HELPER_3(neon_cge_f32, i32, i32, i32, ptr)
@@ -463,4 +489,11 @@ DEF_HELPER_3(neon_qzip8, void, env, i32, i32)
DEF_HELPER_3(neon_qzip16, void, env, i32, i32)
DEF_HELPER_3(neon_qzip32, void, env, i32, i32)
+DEF_HELPER_4(crypto_aese, void, env, i32, i32, i32)
+DEF_HELPER_4(crypto_aesmc, void, env, i32, i32, i32)
+
+#ifdef TARGET_AARCH64
+#include "helper-a64.h"
+#endif
+
#include "exec/def-helper.h"
diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h
index 2bba0bd198..0e7f889cba 100644
--- a/target-arm/kvm-consts.h
+++ b/target-arm/kvm-consts.h
@@ -29,12 +29,14 @@
#define CP_REG_SIZE_U32 0x0020000000000000ULL
#define CP_REG_SIZE_U64 0x0030000000000000ULL
#define CP_REG_ARM 0x4000000000000000ULL
+#define CP_REG_ARCH_MASK 0xff00000000000000ULL
MISMATCH_CHECK(CP_REG_SIZE_SHIFT, KVM_REG_SIZE_SHIFT)
MISMATCH_CHECK(CP_REG_SIZE_MASK, KVM_REG_SIZE_MASK)
MISMATCH_CHECK(CP_REG_SIZE_U32, KVM_REG_SIZE_U32)
MISMATCH_CHECK(CP_REG_SIZE_U64, KVM_REG_SIZE_U64)
MISMATCH_CHECK(CP_REG_ARM, KVM_REG_ARM)
+MISMATCH_CHECK(CP_REG_ARCH_MASK, KVM_REG_ARCH_MASK)
#define PSCI_FN_BASE 0x95c1ba5e
#define PSCI_FN(n) (PSCI_FN_BASE + (n))
@@ -59,6 +61,41 @@ MISMATCH_CHECK(PSCI_FN_MIGRATE, KVM_PSCI_FN_MIGRATE)
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15)
#endif
+#define CP_REG_ARM64 0x6000000000000000ULL
+#define CP_REG_ARM_COPROC_MASK 0x000000000FFF0000
+#define CP_REG_ARM_COPROC_SHIFT 16
+#define CP_REG_ARM64_SYSREG (0x0013 << CP_REG_ARM_COPROC_SHIFT)
+#define CP_REG_ARM64_SYSREG_OP0_MASK 0x000000000000c000
+#define CP_REG_ARM64_SYSREG_OP0_SHIFT 14
+#define CP_REG_ARM64_SYSREG_OP1_MASK 0x0000000000003800
+#define CP_REG_ARM64_SYSREG_OP1_SHIFT 11
+#define CP_REG_ARM64_SYSREG_CRN_MASK 0x0000000000000780
+#define CP_REG_ARM64_SYSREG_CRN_SHIFT 7
+#define CP_REG_ARM64_SYSREG_CRM_MASK 0x0000000000000078
+#define CP_REG_ARM64_SYSREG_CRM_SHIFT 3
+#define CP_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007
+#define CP_REG_ARM64_SYSREG_OP2_SHIFT 0
+
+/* No kernel define but it's useful to QEMU */
+#define CP_REG_ARM64_SYSREG_CP (CP_REG_ARM64_SYSREG >> CP_REG_ARM_COPROC_SHIFT)
+
+#ifdef TARGET_AARCH64
+MISMATCH_CHECK(CP_REG_ARM64, KVM_REG_ARM64)
+MISMATCH_CHECK(CP_REG_ARM_COPROC_MASK, KVM_REG_ARM_COPROC_MASK)
+MISMATCH_CHECK(CP_REG_ARM_COPROC_SHIFT, KVM_REG_ARM_COPROC_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG, KVM_REG_ARM64_SYSREG)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_MASK, KVM_REG_ARM64_SYSREG_OP0_MASK)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP0_SHIFT, KVM_REG_ARM64_SYSREG_OP0_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_MASK, KVM_REG_ARM64_SYSREG_OP1_MASK)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP1_SHIFT, KVM_REG_ARM64_SYSREG_OP1_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_MASK, KVM_REG_ARM64_SYSREG_CRN_MASK)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRN_SHIFT, KVM_REG_ARM64_SYSREG_CRN_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_MASK, KVM_REG_ARM64_SYSREG_CRM_MASK)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_CRM_SHIFT, KVM_REG_ARM64_SYSREG_CRM_SHIFT)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_MASK, KVM_REG_ARM64_SYSREG_OP2_MASK)
+MISMATCH_CHECK(CP_REG_ARM64_SYSREG_OP2_SHIFT, KVM_REG_ARM64_SYSREG_OP2_SHIFT)
+#endif
+
#undef MISMATCH_CHECK
#endif
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index f865dac871..1d2688dda7 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -100,120 +100,6 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray)
}
}
-static inline void set_feature(uint64_t *features, int feature)
-{
- *features |= 1ULL << feature;
-}
-
-bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
-{
- /* Identify the feature bits corresponding to the host CPU, and
- * fill out the ARMHostCPUClass fields accordingly. To do this
- * we have to create a scratch VM, create a single CPU inside it,
- * and then query that CPU for the relevant ID registers.
- */
- int i, ret, fdarray[3];
- uint32_t midr, id_pfr0, id_isar0, mvfr1;
- uint64_t features = 0;
- /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
- * we know these will only support creating one kind of guest CPU,
- * which is its preferred CPU type.
- */
- static const uint32_t cpus_to_try[] = {
- QEMU_KVM_ARM_TARGET_CORTEX_A15,
- QEMU_KVM_ARM_TARGET_NONE
- };
- struct kvm_vcpu_init init;
- struct kvm_one_reg idregs[] = {
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
- .addr = (uintptr_t)&midr,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
- .addr = (uintptr_t)&id_pfr0,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
- .addr = (uintptr_t)&id_isar0,
- },
- {
- .id = KVM_REG_ARM | KVM_REG_SIZE_U32
- | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
- .addr = (uintptr_t)&mvfr1,
- },
- };
-
- if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
- return false;
- }
-
- ahcc->target = init.target;
-
- /* This is not strictly blessed by the device tree binding docs yet,
- * but in practice the kernel does not care about this string so
- * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
- */
- ahcc->dtb_compatible = "arm,arm-v7";
-
- for (i = 0; i < ARRAY_SIZE(idregs); i++) {
- ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
- if (ret) {
- break;
- }
- }
-
- kvm_arm_destroy_scratch_host_vcpu(fdarray);
-
- if (ret) {
- return false;
- }
-
- /* Now we've retrieved all the register information we can
- * set the feature bits based on the ID register fields.
- * We can assume any KVM supporting CPU is at least a v7
- * with VFPv3, LPAE and the generic timers; this in turn implies
- * most of the other feature bits, but a few must be tested.
- */
- set_feature(&features, ARM_FEATURE_V7);
- set_feature(&features, ARM_FEATURE_VFP3);
- set_feature(&features, ARM_FEATURE_LPAE);
- set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
-
- switch (extract32(id_isar0, 24, 4)) {
- case 1:
- set_feature(&features, ARM_FEATURE_THUMB_DIV);
- break;
- case 2:
- set_feature(&features, ARM_FEATURE_ARM_DIV);
- set_feature(&features, ARM_FEATURE_THUMB_DIV);
- break;
- default:
- break;
- }
-
- if (extract32(id_pfr0, 12, 4) == 1) {
- set_feature(&features, ARM_FEATURE_THUMB2EE);
- }
- if (extract32(mvfr1, 20, 4) == 1) {
- set_feature(&features, ARM_FEATURE_VFP_FP16);
- }
- if (extract32(mvfr1, 12, 4) == 1) {
- set_feature(&features, ARM_FEATURE_NEON);
- }
- if (extract32(mvfr1, 28, 4) == 1) {
- /* FMAC support implies VFPv4 */
- set_feature(&features, ARM_FEATURE_VFP4);
- }
-
- ahcc->features = features;
-
- return true;
-}
-
static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data)
{
ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc);
@@ -242,7 +128,11 @@ static void kvm_arm_host_cpu_initfn(Object *obj)
static const TypeInfo host_arm_cpu_type_info = {
.name = TYPE_ARM_HOST_CPU,
+#ifdef TARGET_AARCH64
+ .parent = TYPE_AARCH64_CPU,
+#else
.parent = TYPE_ARM_CPU,
+#endif
.instance_init = kvm_arm_host_cpu_initfn,
.class_init = kvm_arm_host_cpu_class_init,
.class_size = sizeof(ARMHostCPUClass),
@@ -265,144 +155,6 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
-static bool reg_syncs_via_tuple_list(uint64_t regidx)
-{
- /* Return true if the regidx is a register we should synchronize
- * via the cpreg_tuples array (ie is not a core reg we sync by
- * hand in kvm_arch_get/put_registers())
- */
- switch (regidx & KVM_REG_ARM_COPROC_MASK) {
- case KVM_REG_ARM_CORE:
- case KVM_REG_ARM_VFP:
- return false;
- default:
- return true;
- }
-}
-
-static int compare_u64(const void *a, const void *b)
-{
- if (*(uint64_t *)a > *(uint64_t *)b) {
- return 1;
- }
- if (*(uint64_t *)a < *(uint64_t *)b) {
- return -1;
- }
- return 0;
-}
-
-int kvm_arch_init_vcpu(CPUState *cs)
-{
- struct kvm_vcpu_init init;
- int i, ret, arraylen;
- uint64_t v;
- struct kvm_one_reg r;
- struct kvm_reg_list rl;
- struct kvm_reg_list *rlp;
- ARMCPU *cpu = ARM_CPU(cs);
-
- if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
- fprintf(stderr, "KVM is not supported for this guest CPU type\n");
- return -EINVAL;
- }
-
- init.target = cpu->kvm_target;
- memset(init.features, 0, sizeof(init.features));
- if (cpu->start_powered_off) {
- init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
- }
- ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
- if (ret) {
- return ret;
- }
- /* Query the kernel to make sure it supports 32 VFP
- * registers: QEMU's "cortex-a15" CPU is always a
- * VFP-D32 core. The simplest way to do this is just
- * to attempt to read register d31.
- */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
- r.addr = (uintptr_t)(&v);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret == -ENOENT) {
- return -EINVAL;
- }
-
- /* Populate the cpreg list based on the kernel's idea
- * of what registers exist (and throw away the TCG-created list).
- */
- rl.n = 0;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
- if (ret != -E2BIG) {
- return ret;
- }
- rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
- rlp->n = rl.n;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
- if (ret) {
- goto out;
- }
- /* Sort the list we get back from the kernel, since cpreg_tuples
- * must be in strictly ascending order.
- */
- qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
-
- for (i = 0, arraylen = 0; i < rlp->n; i++) {
- if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
- continue;
- }
- switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
- case KVM_REG_SIZE_U32:
- case KVM_REG_SIZE_U64:
- break;
- default:
- fprintf(stderr, "Can't handle size of register in kernel list\n");
- ret = -EINVAL;
- goto out;
- }
-
- arraylen++;
- }
-
- cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
- cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
- cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
- arraylen);
- cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
- arraylen);
- cpu->cpreg_array_len = arraylen;
- cpu->cpreg_vmstate_array_len = arraylen;
-
- for (i = 0, arraylen = 0; i < rlp->n; i++) {
- uint64_t regidx = rlp->reg[i];
- if (!reg_syncs_via_tuple_list(regidx)) {
- continue;
- }
- cpu->cpreg_indexes[arraylen] = regidx;
- arraylen++;
- }
- assert(cpu->cpreg_array_len == arraylen);
-
- if (!write_kvmstate_to_list(cpu)) {
- /* Shouldn't happen unless kernel is inconsistent about
- * what registers exist.
- */
- fprintf(stderr, "Initial read of kernel register state failed\n");
- ret = -EINVAL;
- goto out;
- }
-
- /* Save a copy of the initial register values so that we can
- * feed it back to the kernel on VCPU reset.
- */
- cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
- cpu->cpreg_array_len *
- sizeof(cpu->cpreg_values[0]));
-
-out:
- g_free(rlp);
- return ret;
-}
-
/* We track all the KVM devices which need their memory addresses
* passing to the kernel in a list of these structures.
* When board init is complete we run through the list and
@@ -563,232 +315,6 @@ bool write_list_to_kvmstate(ARMCPU *cpu)
return ok;
}
-typedef struct Reg {
- uint64_t id;
- int offset;
-} Reg;
-
-#define COREREG(KERNELNAME, QEMUFIELD) \
- { \
- KVM_REG_ARM | KVM_REG_SIZE_U32 | \
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
- offsetof(CPUARMState, QEMUFIELD) \
- }
-
-#define VFPSYSREG(R) \
- { \
- KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
- KVM_REG_ARM_VFP_##R, \
- offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
- }
-
-static const Reg regs[] = {
- /* R0_usr .. R14_usr */
- COREREG(usr_regs.uregs[0], regs[0]),
- COREREG(usr_regs.uregs[1], regs[1]),
- COREREG(usr_regs.uregs[2], regs[2]),
- COREREG(usr_regs.uregs[3], regs[3]),
- COREREG(usr_regs.uregs[4], regs[4]),
- COREREG(usr_regs.uregs[5], regs[5]),
- COREREG(usr_regs.uregs[6], regs[6]),
- COREREG(usr_regs.uregs[7], regs[7]),
- COREREG(usr_regs.uregs[8], usr_regs[0]),
- COREREG(usr_regs.uregs[9], usr_regs[1]),
- COREREG(usr_regs.uregs[10], usr_regs[2]),
- COREREG(usr_regs.uregs[11], usr_regs[3]),
- COREREG(usr_regs.uregs[12], usr_regs[4]),
- COREREG(usr_regs.uregs[13], banked_r13[0]),
- COREREG(usr_regs.uregs[14], banked_r14[0]),
- /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
- COREREG(svc_regs[0], banked_r13[1]),
- COREREG(svc_regs[1], banked_r14[1]),
- COREREG(svc_regs[2], banked_spsr[1]),
- COREREG(abt_regs[0], banked_r13[2]),
- COREREG(abt_regs[1], banked_r14[2]),
- COREREG(abt_regs[2], banked_spsr[2]),
- COREREG(und_regs[0], banked_r13[3]),
- COREREG(und_regs[1], banked_r14[3]),
- COREREG(und_regs[2], banked_spsr[3]),
- COREREG(irq_regs[0], banked_r13[4]),
- COREREG(irq_regs[1], banked_r14[4]),
- COREREG(irq_regs[2], banked_spsr[4]),
- /* R8_fiq .. R14_fiq and SPSR_fiq */
- COREREG(fiq_regs[0], fiq_regs[0]),
- COREREG(fiq_regs[1], fiq_regs[1]),
- COREREG(fiq_regs[2], fiq_regs[2]),
- COREREG(fiq_regs[3], fiq_regs[3]),
- COREREG(fiq_regs[4], fiq_regs[4]),
- COREREG(fiq_regs[5], banked_r13[5]),
- COREREG(fiq_regs[6], banked_r14[5]),
- COREREG(fiq_regs[7], banked_spsr[5]),
- /* R15 */
- COREREG(usr_regs.uregs[15], regs[15]),
- /* VFP system registers */
- VFPSYSREG(FPSID),
- VFPSYSREG(MVFR1),
- VFPSYSREG(MVFR0),
- VFPSYSREG(FPEXC),
- VFPSYSREG(FPINST),
- VFPSYSREG(FPINST2),
-};
-
-int kvm_arch_put_registers(CPUState *cs, int level)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
- struct kvm_one_reg r;
- int mode, bn;
- int ret, i;
- uint32_t cpsr, fpscr;
-
- /* Make sure the banked regs are properly set */
- mode = env->uncached_cpsr & CPSR_M;
- bn = bank_number(mode);
- if (mode == ARM_CPU_MODE_FIQ) {
- memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
- } else {
- memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
- }
- env->banked_r13[bn] = env->regs[13];
- env->banked_r14[bn] = env->regs[14];
- env->banked_spsr[bn] = env->spsr;
-
- /* Now we can safely copy stuff down to the kernel */
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- r.id = regs[i].id;
- r.addr = (uintptr_t)(env) + regs[i].offset;
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- }
-
- /* Special cases which aren't a single CPUARMState field */
- cpsr = cpsr_read(env);
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
- r.addr = (uintptr_t)(&cpsr);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
- /* VFP registers */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
- for (i = 0; i < 32; i++) {
- r.addr = (uintptr_t)(&env->vfp.regs[i]);
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- r.id++;
- }
-
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
- KVM_REG_ARM_VFP_FPSCR;
- fpscr = vfp_get_fpscr(env);
- r.addr = (uintptr_t)&fpscr;
- ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
-
- /* Note that we do not call write_cpustate_to_list()
- * here, so we are only writing the tuple list back to
- * KVM. This is safe because nothing can change the
- * CPUARMState cp15 fields (in particular gdb accesses cannot)
- * and so there are no changes to sync. In fact syncing would
- * be wrong at this point: for a constant register where TCG and
- * KVM disagree about its value, the preceding write_list_to_cpustate()
- * would not have had any effect on the CPUARMState value (since the
- * register is read-only), and a write_cpustate_to_list() here would
- * then try to write the TCG value back into KVM -- this would either
- * fail or incorrectly change the value the guest sees.
- *
- * If we ever want to allow the user to modify cp15 registers via
- * the gdb stub, we would need to be more clever here (for instance
- * tracking the set of registers kvm_arch_get_registers() successfully
- * managed to update the CPUARMState with, and only allowing those
- * to be written back up into the kernel).
- */
- if (!write_list_to_kvmstate(cpu)) {
- return EINVAL;
- }
-
- return ret;
-}
-
-int kvm_arch_get_registers(CPUState *cs)
-{
- ARMCPU *cpu = ARM_CPU(cs);
- CPUARMState *env = &cpu->env;
- struct kvm_one_reg r;
- int mode, bn;
- int ret, i;
- uint32_t cpsr, fpscr;
-
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- r.id = regs[i].id;
- r.addr = (uintptr_t)(env) + regs[i].offset;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- }
-
- /* Special cases which aren't a single CPUARMState field */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
- KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
- r.addr = (uintptr_t)(&cpsr);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- cpsr_write(env, cpsr, 0xffffffff);
-
- /* Make sure the current mode regs are properly set */
- mode = env->uncached_cpsr & CPSR_M;
- bn = bank_number(mode);
- if (mode == ARM_CPU_MODE_FIQ) {
- memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
- } else {
- memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
- }
- env->regs[13] = env->banked_r13[bn];
- env->regs[14] = env->banked_r14[bn];
- env->spsr = env->banked_spsr[bn];
-
- /* VFP registers */
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
- for (i = 0; i < 32; i++) {
- r.addr = (uintptr_t)(&env->vfp.regs[i]);
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- r.id++;
- }
-
- r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
- KVM_REG_ARM_VFP_FPSCR;
- r.addr = (uintptr_t)&fpscr;
- ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
- if (ret) {
- return ret;
- }
- vfp_set_fpscr(env, fpscr);
-
- if (!write_kvmstate_to_list(cpu)) {
- return EINVAL;
- }
- /* Note that it's OK to have registers which aren't in CPUState,
- * so we can ignore a failure return here.
- */
- write_list_to_cpustate(cpu);
-
- return 0;
-}
-
void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
{
}
@@ -802,19 +328,6 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
return 0;
}
-void kvm_arch_reset_vcpu(CPUState *cs)
-{
- /* Feed the kernel back its initial register state */
- ARMCPU *cpu = ARM_CPU(cs);
-
- memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
- cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
-
- if (!write_list_to_kvmstate(cpu)) {
- abort();
- }
-}
-
bool kvm_arch_stop_on_emulation_error(CPUState *cs)
{
return true;
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
new file mode 100644
index 0000000000..a4fde07969
--- /dev/null
+++ b/target-arm/kvm32.c
@@ -0,0 +1,515 @@
+/*
+ * ARM implementation of KVM hooks, 32 bit specific code.
+ *
+ * Copyright Christoffer Dall 2009-2010
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "cpu.h"
+#include "hw/arm/arm.h"
+
+static inline void set_feature(uint64_t *features, int feature)
+{
+ *features |= 1ULL << feature;
+}
+
+bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+{
+ /* Identify the feature bits corresponding to the host CPU, and
+ * fill out the ARMHostCPUClass fields accordingly. To do this
+ * we have to create a scratch VM, create a single CPU inside it,
+ * and then query that CPU for the relevant ID registers.
+ */
+ int i, ret, fdarray[3];
+ uint32_t midr, id_pfr0, id_isar0, mvfr1;
+ uint64_t features = 0;
+ /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
+ * we know these will only support creating one kind of guest CPU,
+ * which is its preferred CPU type.
+ */
+ static const uint32_t cpus_to_try[] = {
+ QEMU_KVM_ARM_TARGET_CORTEX_A15,
+ QEMU_KVM_ARM_TARGET_NONE
+ };
+ struct kvm_vcpu_init init;
+ struct kvm_one_reg idregs[] = {
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 0, 0, 0),
+ .addr = (uintptr_t)&midr,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 1, 0, 0),
+ .addr = (uintptr_t)&id_pfr0,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | ENCODE_CP_REG(15, 0, 0, 2, 0, 0),
+ .addr = (uintptr_t)&id_isar0,
+ },
+ {
+ .id = KVM_REG_ARM | KVM_REG_SIZE_U32
+ | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1,
+ .addr = (uintptr_t)&mvfr1,
+ },
+ };
+
+ if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
+ return false;
+ }
+
+ ahcc->target = init.target;
+
+ /* This is not strictly blessed by the device tree binding docs yet,
+ * but in practice the kernel does not care about this string so
+ * there is no point maintaining an KVM_ARM_TARGET_* -> string table.
+ */
+ ahcc->dtb_compatible = "arm,arm-v7";
+
+ for (i = 0; i < ARRAY_SIZE(idregs); i++) {
+ ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]);
+ if (ret) {
+ break;
+ }
+ }
+
+ kvm_arm_destroy_scratch_host_vcpu(fdarray);
+
+ if (ret) {
+ return false;
+ }
+
+ /* Now we've retrieved all the register information we can
+ * set the feature bits based on the ID register fields.
+ * We can assume any KVM supporting CPU is at least a v7
+ * with VFPv3, LPAE and the generic timers; this in turn implies
+ * most of the other feature bits, but a few must be tested.
+ */
+ set_feature(&features, ARM_FEATURE_V7);
+ set_feature(&features, ARM_FEATURE_VFP3);
+ set_feature(&features, ARM_FEATURE_LPAE);
+ set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
+
+ switch (extract32(id_isar0, 24, 4)) {
+ case 1:
+ set_feature(&features, ARM_FEATURE_THUMB_DIV);
+ break;
+ case 2:
+ set_feature(&features, ARM_FEATURE_ARM_DIV);
+ set_feature(&features, ARM_FEATURE_THUMB_DIV);
+ break;
+ default:
+ break;
+ }
+
+ if (extract32(id_pfr0, 12, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_THUMB2EE);
+ }
+ if (extract32(mvfr1, 20, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_VFP_FP16);
+ }
+ if (extract32(mvfr1, 12, 4) == 1) {
+ set_feature(&features, ARM_FEATURE_NEON);
+ }
+ if (extract32(mvfr1, 28, 4) == 1) {
+ /* FMAC support implies VFPv4 */
+ set_feature(&features, ARM_FEATURE_VFP4);
+ }
+
+ ahcc->features = features;
+
+ return true;
+}
+
+static bool reg_syncs_via_tuple_list(uint64_t regidx)
+{
+ /* Return true if the regidx is a register we should synchronize
+ * via the cpreg_tuples array (ie is not a core reg we sync by
+ * hand in kvm_arch_get/put_registers())
+ */
+ switch (regidx & KVM_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE:
+ case KVM_REG_ARM_VFP:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int compare_u64(const void *a, const void *b)
+{
+ if (*(uint64_t *)a > *(uint64_t *)b) {
+ return 1;
+ }
+ if (*(uint64_t *)a < *(uint64_t *)b) {
+ return -1;
+ }
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ struct kvm_vcpu_init init;
+ int i, ret, arraylen;
+ uint64_t v;
+ struct kvm_one_reg r;
+ struct kvm_reg_list rl;
+ struct kvm_reg_list *rlp;
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) {
+ fprintf(stderr, "KVM is not supported for this guest CPU type\n");
+ return -EINVAL;
+ }
+
+ init.target = cpu->kvm_target;
+ memset(init.features, 0, sizeof(init.features));
+ if (cpu->start_powered_off) {
+ init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
+ }
+ ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
+ if (ret) {
+ return ret;
+ }
+ /* Query the kernel to make sure it supports 32 VFP
+ * registers: QEMU's "cortex-a15" CPU is always a
+ * VFP-D32 core. The simplest way to do this is just
+ * to attempt to read register d31.
+ */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31;
+ r.addr = (uintptr_t)(&v);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret == -ENOENT) {
+ return -EINVAL;
+ }
+
+ /* Populate the cpreg list based on the kernel's idea
+ * of what registers exist (and throw away the TCG-created list).
+ */
+ rl.n = 0;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, &rl);
+ if (ret != -E2BIG) {
+ return ret;
+ }
+ rlp = g_malloc(sizeof(struct kvm_reg_list) + rl.n * sizeof(uint64_t));
+ rlp->n = rl.n;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REG_LIST, rlp);
+ if (ret) {
+ goto out;
+ }
+ /* Sort the list we get back from the kernel, since cpreg_tuples
+ * must be in strictly ascending order.
+ */
+ qsort(&rlp->reg, rlp->n, sizeof(rlp->reg[0]), compare_u64);
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ if (!reg_syncs_via_tuple_list(rlp->reg[i])) {
+ continue;
+ }
+ switch (rlp->reg[i] & KVM_REG_SIZE_MASK) {
+ case KVM_REG_SIZE_U32:
+ case KVM_REG_SIZE_U64:
+ break;
+ default:
+ fprintf(stderr, "Can't handle size of register in kernel list\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ arraylen++;
+ }
+
+ cpu->cpreg_indexes = g_renew(uint64_t, cpu->cpreg_indexes, arraylen);
+ cpu->cpreg_values = g_renew(uint64_t, cpu->cpreg_values, arraylen);
+ cpu->cpreg_vmstate_indexes = g_renew(uint64_t, cpu->cpreg_vmstate_indexes,
+ arraylen);
+ cpu->cpreg_vmstate_values = g_renew(uint64_t, cpu->cpreg_vmstate_values,
+ arraylen);
+ cpu->cpreg_array_len = arraylen;
+ cpu->cpreg_vmstate_array_len = arraylen;
+
+ for (i = 0, arraylen = 0; i < rlp->n; i++) {
+ uint64_t regidx = rlp->reg[i];
+ if (!reg_syncs_via_tuple_list(regidx)) {
+ continue;
+ }
+ cpu->cpreg_indexes[arraylen] = regidx;
+ arraylen++;
+ }
+ assert(cpu->cpreg_array_len == arraylen);
+
+ if (!write_kvmstate_to_list(cpu)) {
+ /* Shouldn't happen unless kernel is inconsistent about
+ * what registers exist.
+ */
+ fprintf(stderr, "Initial read of kernel register state failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Save a copy of the initial register values so that we can
+ * feed it back to the kernel on VCPU reset.
+ */
+ cpu->cpreg_reset_values = g_memdup(cpu->cpreg_values,
+ cpu->cpreg_array_len *
+ sizeof(cpu->cpreg_values[0]));
+
+out:
+ g_free(rlp);
+ return ret;
+}
+
+typedef struct Reg {
+ uint64_t id;
+ int offset;
+} Reg;
+
+#define COREREG(KERNELNAME, QEMUFIELD) \
+ { \
+ KVM_REG_ARM | KVM_REG_SIZE_U32 | \
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \
+ offsetof(CPUARMState, QEMUFIELD) \
+ }
+
+#define VFPSYSREG(R) \
+ { \
+ KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \
+ KVM_REG_ARM_VFP_##R, \
+ offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \
+ }
+
+static const Reg regs[] = {
+ /* R0_usr .. R14_usr */
+ COREREG(usr_regs.uregs[0], regs[0]),
+ COREREG(usr_regs.uregs[1], regs[1]),
+ COREREG(usr_regs.uregs[2], regs[2]),
+ COREREG(usr_regs.uregs[3], regs[3]),
+ COREREG(usr_regs.uregs[4], regs[4]),
+ COREREG(usr_regs.uregs[5], regs[5]),
+ COREREG(usr_regs.uregs[6], regs[6]),
+ COREREG(usr_regs.uregs[7], regs[7]),
+ COREREG(usr_regs.uregs[8], usr_regs[0]),
+ COREREG(usr_regs.uregs[9], usr_regs[1]),
+ COREREG(usr_regs.uregs[10], usr_regs[2]),
+ COREREG(usr_regs.uregs[11], usr_regs[3]),
+ COREREG(usr_regs.uregs[12], usr_regs[4]),
+ COREREG(usr_regs.uregs[13], banked_r13[0]),
+ COREREG(usr_regs.uregs[14], banked_r14[0]),
+ /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */
+ COREREG(svc_regs[0], banked_r13[1]),
+ COREREG(svc_regs[1], banked_r14[1]),
+ COREREG(svc_regs[2], banked_spsr[1]),
+ COREREG(abt_regs[0], banked_r13[2]),
+ COREREG(abt_regs[1], banked_r14[2]),
+ COREREG(abt_regs[2], banked_spsr[2]),
+ COREREG(und_regs[0], banked_r13[3]),
+ COREREG(und_regs[1], banked_r14[3]),
+ COREREG(und_regs[2], banked_spsr[3]),
+ COREREG(irq_regs[0], banked_r13[4]),
+ COREREG(irq_regs[1], banked_r14[4]),
+ COREREG(irq_regs[2], banked_spsr[4]),
+ /* R8_fiq .. R14_fiq and SPSR_fiq */
+ COREREG(fiq_regs[0], fiq_regs[0]),
+ COREREG(fiq_regs[1], fiq_regs[1]),
+ COREREG(fiq_regs[2], fiq_regs[2]),
+ COREREG(fiq_regs[3], fiq_regs[3]),
+ COREREG(fiq_regs[4], fiq_regs[4]),
+ COREREG(fiq_regs[5], banked_r13[5]),
+ COREREG(fiq_regs[6], banked_r14[5]),
+ COREREG(fiq_regs[7], banked_spsr[5]),
+ /* R15 */
+ COREREG(usr_regs.uregs[15], regs[15]),
+ /* VFP system registers */
+ VFPSYSREG(FPSID),
+ VFPSYSREG(MVFR1),
+ VFPSYSREG(MVFR0),
+ VFPSYSREG(FPEXC),
+ VFPSYSREG(FPINST),
+ VFPSYSREG(FPINST2),
+};
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ struct kvm_one_reg r;
+ int mode, bn;
+ int ret, i;
+ uint32_t cpsr, fpscr;
+
+ /* Make sure the banked regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(mode);
+ if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ } else {
+ memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ }
+ env->banked_r13[bn] = env->regs[13];
+ env->banked_r14[bn] = env->regs[14];
+ env->banked_spsr[bn] = env->spsr;
+
+ /* Now we can safely copy stuff down to the kernel */
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ r.id = regs[i].id;
+ r.addr = (uintptr_t)(env) + regs[i].offset;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /* Special cases which aren't a single CPUARMState field */
+ cpsr = cpsr_read(env);
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
+ r.addr = (uintptr_t)(&cpsr);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+
+ /* VFP registers */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
+ for (i = 0; i < 32; i++) {
+ r.addr = (uintptr_t)(&env->vfp.regs[i]);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ r.id++;
+ }
+
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
+ KVM_REG_ARM_VFP_FPSCR;
+ fpscr = vfp_get_fpscr(env);
+ r.addr = (uintptr_t)&fpscr;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+
+ /* Note that we do not call write_cpustate_to_list()
+ * here, so we are only writing the tuple list back to
+ * KVM. This is safe because nothing can change the
+ * CPUARMState cp15 fields (in particular gdb accesses cannot)
+ * and so there are no changes to sync. In fact syncing would
+ * be wrong at this point: for a constant register where TCG and
+ * KVM disagree about its value, the preceding write_list_to_cpustate()
+ * would not have had any effect on the CPUARMState value (since the
+ * register is read-only), and a write_cpustate_to_list() here would
+ * then try to write the TCG value back into KVM -- this would either
+ * fail or incorrectly change the value the guest sees.
+ *
+ * If we ever want to allow the user to modify cp15 registers via
+ * the gdb stub, we would need to be more clever here (for instance
+ * tracking the set of registers kvm_arch_get_registers() successfully
+ * managed to update the CPUARMState with, and only allowing those
+ * to be written back up into the kernel).
+ */
+ if (!write_list_to_kvmstate(cpu)) {
+ return EINVAL;
+ }
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+ struct kvm_one_reg r;
+ int mode, bn;
+ int ret, i;
+ uint32_t cpsr, fpscr;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ r.id = regs[i].id;
+ r.addr = (uintptr_t)(env) + regs[i].offset;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /* Special cases which aren't a single CPUARMState field */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 |
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr);
+ r.addr = (uintptr_t)(&cpsr);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ cpsr_write(env, cpsr, 0xffffffff);
+
+ /* Make sure the current mode regs are properly set */
+ mode = env->uncached_cpsr & CPSR_M;
+ bn = bank_number(mode);
+ if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+ } else {
+ memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+ }
+ env->regs[13] = env->banked_r13[bn];
+ env->regs[14] = env->banked_r14[bn];
+ env->spsr = env->banked_spsr[bn];
+
+ /* VFP registers */
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP;
+ for (i = 0; i < 32; i++) {
+ r.addr = (uintptr_t)(&env->vfp.regs[i]);
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ r.id++;
+ }
+
+ r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP |
+ KVM_REG_ARM_VFP_FPSCR;
+ r.addr = (uintptr_t)&fpscr;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r);
+ if (ret) {
+ return ret;
+ }
+ vfp_set_fpscr(env, fpscr);
+
+ if (!write_kvmstate_to_list(cpu)) {
+ return EINVAL;
+ }
+ /* Note that it's OK to have registers which aren't in CPUState,
+ * so we can ignore a failure return here.
+ */
+ write_list_to_cpustate(cpu);
+
+ return 0;
+}
+
+void kvm_arch_reset_vcpu(CPUState *cs)
+{
+ /* Feed the kernel back its initial register state */
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ memmove(cpu->cpreg_values, cpu->cpreg_reset_values,
+ cpu->cpreg_array_len * sizeof(cpu->cpreg_values[0]));
+
+ if (!write_list_to_kvmstate(cpu)) {
+ abort();
+ }
+}
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
new file mode 100644
index 0000000000..1b7ca90374
--- /dev/null
+++ b/target-arm/kvm64.c
@@ -0,0 +1,204 @@
+/*
+ * ARM implementation of KVM hooks, 64 bit specific code
+ *
+ * Copyright Mian-M. Hamayun 2013, Virtual Open Systems
+ *
+ * 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "cpu.h"
+#include "hw/arm/arm.h"
+
+static inline void set_feature(uint64_t *features, int feature)
+{
+ *features |= 1ULL << feature;
+}
+
+bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
+{
+ /* Identify the feature bits corresponding to the host CPU, and
+ * fill out the ARMHostCPUClass fields accordingly. To do this
+ * we have to create a scratch VM, create a single CPU inside it,
+ * and then query that CPU for the relevant ID registers.
+ * For AArch64 we currently don't care about ID registers at
+ * all; we just want to know the CPU type.
+ */
+ int fdarray[3];
+ uint64_t features = 0;
+ /* Old kernels may not know about the PREFERRED_TARGET ioctl: however
+ * we know these will only support creating one kind of guest CPU,
+ * which is its preferred CPU type. Fortunately these old kernels
+ * support only a very limited number of CPUs.
+ */
+ static const uint32_t cpus_to_try[] = {
+ KVM_ARM_TARGET_AEM_V8,
+ KVM_ARM_TARGET_FOUNDATION_V8,
+ KVM_ARM_TARGET_CORTEX_A57,
+ QEMU_KVM_ARM_TARGET_NONE
+ };
+ struct kvm_vcpu_init init;
+
+ if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
+ return false;
+ }
+
+ ahcc->target = init.target;
+ ahcc->dtb_compatible = "arm,arm-v8";
+
+ kvm_arm_destroy_scratch_host_vcpu(fdarray);
+
+ /* We can assume any KVM supporting CPU is at least a v8
+ * with VFPv4+Neon; this in turn implies most of the other
+ * feature bits.
+ */
+ set_feature(&features, ARM_FEATURE_V8);
+ set_feature(&features, ARM_FEATURE_VFP4);
+ set_feature(&features, ARM_FEATURE_NEON);
+ set_feature(&features, ARM_FEATURE_AARCH64);
+
+ ahcc->features = features;
+
+ return true;
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ struct kvm_vcpu_init init;
+ int ret;
+
+ if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
+ !arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ fprintf(stderr, "KVM is not supported for this guest CPU type\n");
+ return -EINVAL;
+ }
+
+ init.target = cpu->kvm_target;
+ memset(init.features, 0, sizeof(init.features));
+ if (cpu->start_powered_off) {
+ init.features[0] = 1 << KVM_ARM_VCPU_POWER_OFF;
+ }
+ ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init);
+
+ /* TODO : support for save/restore/reset of system regs via tuple list */
+
+ return ret;
+}
+
+#define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
+ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ struct kvm_one_reg reg;
+ uint64_t val;
+ int i;
+ int ret;
+
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ for (i = 0; i < 31; i++) {
+ reg.id = AARCH64_CORE_REG(regs.regs[i]);
+ reg.addr = (uintptr_t) &env->xregs[i];
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.sp);
+ reg.addr = (uintptr_t) &env->xregs[31];
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+
+ /* Note that KVM thinks pstate is 64 bit but we use a uint32_t */
+ val = pstate_read(env);
+ reg.id = AARCH64_CORE_REG(regs.pstate);
+ reg.addr = (uintptr_t) &val;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.pc);
+ reg.addr = (uintptr_t) &env->pc;
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+
+ /* TODO:
+ * SP_EL1
+ * ELR_EL1
+ * SPSR[]
+ * FP state
+ * system registers
+ */
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ struct kvm_one_reg reg;
+ uint64_t val;
+ int i;
+ int ret;
+
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ for (i = 0; i < 31; i++) {
+ reg.id = AARCH64_CORE_REG(regs.regs[i]);
+ reg.addr = (uintptr_t) &env->xregs[i];
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.sp);
+ reg.addr = (uintptr_t) &env->xregs[31];
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+
+ reg.id = AARCH64_CORE_REG(regs.pstate);
+ reg.addr = (uintptr_t) &val;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+ pstate_write(env, val);
+
+ reg.id = AARCH64_CORE_REG(regs.pc);
+ reg.addr = (uintptr_t) &env->pc;
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+ if (ret) {
+ return ret;
+ }
+
+ /* TODO: other registers */
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *cs)
+{
+}
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 74f010f637..8f9e7d4d28 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_id)
const VMStateDescription vmstate_arm_cpu = {
.name = "cpu",
- .version_id = 13,
- .minimum_version_id = 13,
- .minimum_version_id_old = 13,
+ .version_id = 14,
+ .minimum_version_id = 14,
+ .minimum_version_id_old = 14,
.pre_save = cpu_pre_save,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
@@ -253,9 +253,9 @@ const VMStateDescription vmstate_arm_cpu = {
VMSTATE_VARRAY_INT32(cpreg_vmstate_values, ARMCPU,
cpreg_vmstate_array_len,
0, vmstate_info_uint64, uint64_t),
- VMSTATE_UINT32(env.exclusive_addr, ARMCPU),
- VMSTATE_UINT32(env.exclusive_val, ARMCPU),
- VMSTATE_UINT32(env.exclusive_high, ARMCPU),
+ VMSTATE_UINT64(env.exclusive_addr, ARMCPU),
+ VMSTATE_UINT64(env.exclusive_val, ARMCPU),
+ VMSTATE_UINT64(env.exclusive_high, ARMCPU),
VMSTATE_UINT64(env.features, ARMCPU),
VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU),
VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU),
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
index b028cc2c93..be6fbd997e 100644
--- a/target-arm/neon_helper.c
+++ b/target-arm/neon_helper.c
@@ -1765,18 +1765,6 @@ uint32_t HELPER(neon_qneg_s32)(CPUARMState *env, uint32_t x)
}
/* NEON Float helpers. */
-uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b, void *fpstp)
-{
- float_status *fpst = fpstp;
- return float32_val(float32_min(make_float32(a), make_float32(b), fpst));
-}
-
-uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b, void *fpstp)
-{
- float_status *fpst = fpstp;
- return float32_val(float32_max(make_float32(a), make_float32(b), fpst));
-}
-
uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b, void *fpstp)
{
float_status *fpst = fpstp;
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index f120088607..cf80c46b90 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -28,13 +28,24 @@
#include "translate.h"
#include "qemu/host-utils.h"
+#include "exec/gen-icount.h"
+
#include "helper.h"
#define GEN_HELPER 1
#include "helper.h"
static TCGv_i64 cpu_X[32];
static TCGv_i64 cpu_pc;
-static TCGv_i32 pstate;
+static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF;
+
+/* Load/store exclusive handling */
+static TCGv_i64 cpu_exclusive_addr;
+static TCGv_i64 cpu_exclusive_val;
+static TCGv_i64 cpu_exclusive_high;
+#ifdef CONFIG_USER_ONLY
+static TCGv_i64 cpu_exclusive_test;
+static TCGv_i32 cpu_exclusive_info;
+#endif
static const char *regnames[] = {
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
@@ -43,6 +54,13 @@ static const char *regnames[] = {
"x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp"
};
+enum a64_shift_type {
+ A64_SHIFT_TYPE_LSL = 0,
+ A64_SHIFT_TYPE_LSR = 1,
+ A64_SHIFT_TYPE_ASR = 2,
+ A64_SHIFT_TYPE_ROR = 3
+};
+
/* initialize TCG globals. */
void a64_translate_init(void)
{
@@ -57,9 +75,23 @@ void a64_translate_init(void)
regnames[i]);
}
- pstate = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUARMState, pstate),
- "pstate");
+ cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF");
+ cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF");
+ cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF");
+ cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF");
+
+ cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
+ cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, exclusive_val), "exclusive_val");
+ cpu_exclusive_high = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, exclusive_high), "exclusive_high");
+#ifdef CONFIG_USER_ONLY
+ cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUARMState, exclusive_test), "exclusive_test");
+ cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUARMState, exclusive_info), "exclusive_info");
+#endif
}
void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
@@ -67,6 +99,7 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
+ uint32_t psr = pstate_read(env);
int i;
cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n",
@@ -79,12 +112,38 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f,
cpu_fprintf(f, " ");
}
}
- cpu_fprintf(f, "PSTATE=%c%c%c%c\n",
- env->pstate & PSTATE_N ? 'n' : '.',
- env->pstate & PSTATE_Z ? 'z' : '.',
- env->pstate & PSTATE_C ? 'c' : '.',
- env->pstate & PSTATE_V ? 'v' : '.');
+ cpu_fprintf(f, "PSTATE=%08x (flags %c%c%c%c)\n",
+ psr,
+ psr & PSTATE_N ? 'N' : '-',
+ psr & PSTATE_Z ? 'Z' : '-',
+ psr & PSTATE_C ? 'C' : '-',
+ psr & PSTATE_V ? 'V' : '-');
cpu_fprintf(f, "\n");
+
+ if (flags & CPU_DUMP_FPU) {
+ int numvfpregs = 32;
+ for (i = 0; i < numvfpregs; i += 2) {
+ uint64_t vlo = float64_val(env->vfp.regs[i * 2]);
+ uint64_t vhi = float64_val(env->vfp.regs[(i * 2) + 1]);
+ cpu_fprintf(f, "q%02d=%016" PRIx64 ":%016" PRIx64 " ",
+ i, vhi, vlo);
+ vlo = float64_val(env->vfp.regs[(i + 1) * 2]);
+ vhi = float64_val(env->vfp.regs[((i + 1) * 2) + 1]);
+ cpu_fprintf(f, "q%02d=%016" PRIx64 ":%016" PRIx64 "\n",
+ i + 1, vhi, vlo);
+ }
+ cpu_fprintf(f, "FPCR: %08x FPSR: %08x\n",
+ vfp_get_fpcr(env), vfp_get_fpsr(env));
+ }
+}
+
+static int get_mem_index(DisasContext *s)
+{
+#ifdef CONFIG_USER_ONLY
+ return 1;
+#else
+ return s->user;
+#endif
}
void gen_a64_set_pc_im(uint64_t val)
@@ -104,21 +163,4089 @@ static void gen_exception_insn(DisasContext *s, int offset, int excp)
{
gen_a64_set_pc_im(s->pc - offset);
gen_exception(excp);
- s->is_jmp = DISAS_JUMP;
+ s->is_jmp = DISAS_EXC;
+}
+
+static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest)
+{
+ /* No direct tb linking with singlestep or deterministic io */
+ if (s->singlestep_enabled || (s->tb->cflags & CF_LAST_IO)) {
+ return false;
+ }
+
+ /* Only link tbs from inside the same guest page */
+ if ((s->tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
+ return false;
+ }
+
+ return true;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if (use_goto_tb(s, n, dest)) {
+ tcg_gen_goto_tb(n);
+ gen_a64_set_pc_im(dest);
+ tcg_gen_exit_tb((tcg_target_long)tb + n);
+ s->is_jmp = DISAS_TB_JUMP;
+ } else {
+ gen_a64_set_pc_im(dest);
+ if (s->singlestep_enabled) {
+ gen_exception(EXCP_DEBUG);
+ }
+ tcg_gen_exit_tb(0);
+ s->is_jmp = DISAS_JUMP;
+ }
}
-static void real_unallocated_encoding(DisasContext *s)
+static void unallocated_encoding(DisasContext *s)
{
- fprintf(stderr, "Unknown instruction: %#x\n", s->insn);
gen_exception_insn(s, 4, EXCP_UDEF);
}
-#define unallocated_encoding(s) do { \
- fprintf(stderr, "unallocated encoding at line: %d\n", __LINE__); \
- real_unallocated_encoding(s); \
- } while (0)
+#define unsupported_encoding(s, insn) \
+ do { \
+ qemu_log_mask(LOG_UNIMP, \
+ "%s:%d: unsupported instruction encoding 0x%08x " \
+ "at pc=%016" PRIx64 "\n", \
+ __FILE__, __LINE__, insn, s->pc - 4); \
+ unallocated_encoding(s); \
+ } while (0);
+
+static void init_tmp_a64_array(DisasContext *s)
+{
+#ifdef CONFIG_DEBUG_TCG
+ int i;
+ for (i = 0; i < ARRAY_SIZE(s->tmp_a64); i++) {
+ TCGV_UNUSED_I64(s->tmp_a64[i]);
+ }
+#endif
+ s->tmp_a64_count = 0;
+}
+
+static void free_tmp_a64(DisasContext *s)
+{
+ int i;
+ for (i = 0; i < s->tmp_a64_count; i++) {
+ tcg_temp_free_i64(s->tmp_a64[i]);
+ }
+ init_tmp_a64_array(s);
+}
+
+static TCGv_i64 new_tmp_a64(DisasContext *s)
+{
+ assert(s->tmp_a64_count < TMP_A64_MAX);
+ return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64();
+}
+
+static TCGv_i64 new_tmp_a64_zero(DisasContext *s)
+{
+ TCGv_i64 t = new_tmp_a64(s);
+ tcg_gen_movi_i64(t, 0);
+ return t;
+}
+
+/*
+ * Register access functions
+ *
+ * These functions are used for directly accessing a register in where
+ * changes to the final register value are likely to be made. If you
+ * need to use a register for temporary calculation (e.g. index type
+ * operations) use the read_* form.
+ *
+ * B1.2.1 Register mappings
+ *
+ * In instruction register encoding 31 can refer to ZR (zero register) or
+ * the SP (stack pointer) depending on context. In QEMU's case we map SP
+ * to cpu_X[31] and ZR accesses to a temporary which can be discarded.
+ * This is the point of the _sp forms.
+ */
+static TCGv_i64 cpu_reg(DisasContext *s, int reg)
+{
+ if (reg == 31) {
+ return new_tmp_a64_zero(s);
+ } else {
+ return cpu_X[reg];
+ }
+}
+
+/* register access for when 31 == SP */
+static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg)
+{
+ return cpu_X[reg];
+}
+
+/* read a cpu register in 32bit/64bit mode. Returns a TCGv_i64
+ * representing the register contents. This TCGv is an auto-freed
+ * temporary so it need not be explicitly freed, and may be modified.
+ */
+static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf)
+{
+ TCGv_i64 v = new_tmp_a64(s);
+ if (reg != 31) {
+ if (sf) {
+ tcg_gen_mov_i64(v, cpu_X[reg]);
+ } else {
+ tcg_gen_ext32u_i64(v, cpu_X[reg]);
+ }
+ } else {
+ tcg_gen_movi_i64(v, 0);
+ }
+ return v;
+}
+
+static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf)
+{
+ TCGv_i64 v = new_tmp_a64(s);
+ if (sf) {
+ tcg_gen_mov_i64(v, cpu_X[reg]);
+ } else {
+ tcg_gen_ext32u_i64(v, cpu_X[reg]);
+ }
+ return v;
+}
+
+/* Return the offset into CPUARMState of a slice (from
+ * the least significant end) of FP register Qn (ie
+ * Dn, Sn, Hn or Bn).
+ * (Note that this is not the same mapping as for A32; see cpu.h)
+ */
+static inline int fp_reg_offset(int regno, TCGMemOp size)
+{
+ int offs = offsetof(CPUARMState, vfp.regs[regno * 2]);
+#ifdef HOST_WORDS_BIGENDIAN
+ offs += (8 - (1 << size));
+#endif
+ return offs;
+}
-void disas_a64_insn(CPUARMState *env, DisasContext *s)
+/* Offset of the high half of the 128 bit vector Qn */
+static inline int fp_reg_hi_offset(int regno)
+{
+ return offsetof(CPUARMState, vfp.regs[regno * 2 + 1]);
+}
+
+/* Convenience accessors for reading and writing single and double
+ * FP registers. Writing clears the upper parts of the associated
+ * 128 bit vector register, as required by the architecture.
+ * Note that unlike the GP register accessors, the values returned
+ * by the read functions must be manually freed.
+ */
+static TCGv_i64 read_fp_dreg(DisasContext *s, int reg)
+{
+ TCGv_i64 v = tcg_temp_new_i64();
+
+ tcg_gen_ld_i64(v, cpu_env, fp_reg_offset(reg, MO_64));
+ return v;
+}
+
+static TCGv_i32 read_fp_sreg(DisasContext *s, int reg)
+{
+ TCGv_i32 v = tcg_temp_new_i32();
+
+ tcg_gen_ld_i32(v, cpu_env, fp_reg_offset(reg, MO_32));
+ return v;
+}
+
+static void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v)
+{
+ TCGv_i64 tcg_zero = tcg_const_i64(0);
+
+ tcg_gen_st_i64(v, cpu_env, fp_reg_offset(reg, MO_64));
+ tcg_gen_st_i64(tcg_zero, cpu_env, fp_reg_hi_offset(reg));
+ tcg_temp_free_i64(tcg_zero);
+}
+
+static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v)
+{
+ TCGv_i64 tmp = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tmp, v);
+ write_fp_dreg(s, reg, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+static TCGv_ptr get_fpstatus_ptr(void)
+{
+ TCGv_ptr statusptr = tcg_temp_new_ptr();
+ int offset;
+
+ /* In A64 all instructions (both FP and Neon) use the FPCR;
+ * there is no equivalent of the A32 Neon "standard FPSCR value"
+ * and all operations use vfp.fp_status.
+ */
+ offset = offsetof(CPUARMState, vfp.fp_status);
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset);
+ return statusptr;
+}
+
+/* Set ZF and NF based on a 64 bit result. This is alas fiddlier
+ * than the 32 bit equivalent.
+ */
+static inline void gen_set_NZ64(TCGv_i64 result)
+{
+ TCGv_i64 flag = tcg_temp_new_i64();
+
+ tcg_gen_setcondi_i64(TCG_COND_NE, flag, result, 0);
+ tcg_gen_trunc_i64_i32(cpu_ZF, flag);
+ tcg_gen_shri_i64(flag, result, 32);
+ tcg_gen_trunc_i64_i32(cpu_NF, flag);
+ tcg_temp_free_i64(flag);
+}
+
+/* Set NZCV as for a logical operation: NZ as per result, CV cleared. */
+static inline void gen_logic_CC(int sf, TCGv_i64 result)
+{
+ if (sf) {
+ gen_set_NZ64(result);
+ } else {
+ tcg_gen_trunc_i64_i32(cpu_ZF, result);
+ tcg_gen_trunc_i64_i32(cpu_NF, result);
+ }
+ tcg_gen_movi_i32(cpu_CF, 0);
+ tcg_gen_movi_i32(cpu_VF, 0);
+}
+
+/* dest = T0 + T1; compute C, N, V and Z flags */
+static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
+{
+ if (sf) {
+ TCGv_i64 result, flag, tmp;
+ result = tcg_temp_new_i64();
+ flag = tcg_temp_new_i64();
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_movi_i64(tmp, 0);
+ tcg_gen_add2_i64(result, flag, t0, tmp, t1, tmp);
+
+ tcg_gen_trunc_i64_i32(cpu_CF, flag);
+
+ gen_set_NZ64(result);
+
+ tcg_gen_xor_i64(flag, result, t0);
+ tcg_gen_xor_i64(tmp, t0, t1);
+ tcg_gen_andc_i64(flag, flag, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_gen_shri_i64(flag, flag, 32);
+ tcg_gen_trunc_i64_i32(cpu_VF, flag);
+
+ tcg_gen_mov_i64(dest, result);
+ tcg_temp_free_i64(result);
+ tcg_temp_free_i64(flag);
+ } else {
+ /* 32 bit arithmetic */
+ TCGv_i32 t0_32 = tcg_temp_new_i32();
+ TCGv_i32 t1_32 = tcg_temp_new_i32();
+ TCGv_i32 tmp = tcg_temp_new_i32();
+
+ tcg_gen_movi_i32(tmp, 0);
+ tcg_gen_trunc_i64_i32(t0_32, t0);
+ tcg_gen_trunc_i64_i32(t1_32, t1);
+ tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, t1_32, tmp);
+ tcg_gen_mov_i32(cpu_ZF, cpu_NF);
+ tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32);
+ tcg_gen_xor_i32(tmp, t0_32, t1_32);
+ tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp);
+ tcg_gen_extu_i32_i64(dest, cpu_NF);
+
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(t0_32);
+ tcg_temp_free_i32(t1_32);
+ }
+}
+
+/* dest = T0 - T1; compute C, N, V and Z flags */
+static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
+{
+ if (sf) {
+ /* 64 bit arithmetic */
+ TCGv_i64 result, flag, tmp;
+
+ result = tcg_temp_new_i64();
+ flag = tcg_temp_new_i64();
+ tcg_gen_sub_i64(result, t0, t1);
+
+ gen_set_NZ64(result);
+
+ tcg_gen_setcond_i64(TCG_COND_GEU, flag, t0, t1);
+ tcg_gen_trunc_i64_i32(cpu_CF, flag);
+
+ tcg_gen_xor_i64(flag, result, t0);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_xor_i64(tmp, t0, t1);
+ tcg_gen_and_i64(flag, flag, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_gen_shri_i64(flag, flag, 32);
+ tcg_gen_trunc_i64_i32(cpu_VF, flag);
+ tcg_gen_mov_i64(dest, result);
+ tcg_temp_free_i64(flag);
+ tcg_temp_free_i64(result);
+ } else {
+ /* 32 bit arithmetic */
+ TCGv_i32 t0_32 = tcg_temp_new_i32();
+ TCGv_i32 t1_32 = tcg_temp_new_i32();
+ TCGv_i32 tmp;
+
+ tcg_gen_trunc_i64_i32(t0_32, t0);
+ tcg_gen_trunc_i64_i32(t1_32, t1);
+ tcg_gen_sub_i32(cpu_NF, t0_32, t1_32);
+ tcg_gen_mov_i32(cpu_ZF, cpu_NF);
+ tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0_32, t1_32);
+ tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32);
+ tmp = tcg_temp_new_i32();
+ tcg_gen_xor_i32(tmp, t0_32, t1_32);
+ tcg_temp_free_i32(t0_32);
+ tcg_temp_free_i32(t1_32);
+ tcg_gen_and_i32(cpu_VF, cpu_VF, tmp);
+ tcg_temp_free_i32(tmp);
+ tcg_gen_extu_i32_i64(dest, cpu_NF);
+ }
+}
+
+/* dest = T0 + T1 + CF; do not compute flags. */
+static void gen_adc(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
+{
+ TCGv_i64 flag = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(flag, cpu_CF);
+ tcg_gen_add_i64(dest, t0, t1);
+ tcg_gen_add_i64(dest, dest, flag);
+ tcg_temp_free_i64(flag);
+
+ if (!sf) {
+ tcg_gen_ext32u_i64(dest, dest);
+ }
+}
+
+/* dest = T0 + T1 + CF; compute C, N, V and Z flags. */
+static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1)
+{
+ if (sf) {
+ TCGv_i64 result, cf_64, vf_64, tmp;
+ result = tcg_temp_new_i64();
+ cf_64 = tcg_temp_new_i64();
+ vf_64 = tcg_temp_new_i64();
+ tmp = tcg_const_i64(0);
+
+ tcg_gen_extu_i32_i64(cf_64, cpu_CF);
+ tcg_gen_add2_i64(result, cf_64, t0, tmp, cf_64, tmp);
+ tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, tmp);
+ tcg_gen_trunc_i64_i32(cpu_CF, cf_64);
+ gen_set_NZ64(result);
+
+ tcg_gen_xor_i64(vf_64, result, t0);
+ tcg_gen_xor_i64(tmp, t0, t1);
+ tcg_gen_andc_i64(vf_64, vf_64, tmp);
+ tcg_gen_shri_i64(vf_64, vf_64, 32);
+ tcg_gen_trunc_i64_i32(cpu_VF, vf_64);
+
+ tcg_gen_mov_i64(dest, result);
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(vf_64);
+ tcg_temp_free_i64(cf_64);
+ tcg_temp_free_i64(result);
+ } else {
+ TCGv_i32 t0_32, t1_32, tmp;
+ t0_32 = tcg_temp_new_i32();
+ t1_32 = tcg_temp_new_i32();
+ tmp = tcg_const_i32(0);
+
+ tcg_gen_trunc_i64_i32(t0_32, t0);
+ tcg_gen_trunc_i64_i32(t1_32, t1);
+ tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, tmp, cpu_CF, tmp);
+ tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, tmp);
+
+ tcg_gen_mov_i32(cpu_ZF, cpu_NF);
+ tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32);
+ tcg_gen_xor_i32(tmp, t0_32, t1_32);
+ tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp);
+ tcg_gen_extu_i32_i64(dest, cpu_NF);
+
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(t1_32);
+ tcg_temp_free_i32(t0_32);
+ }
+}
+
+/*
+ * Load/Store generators
+ */
+
+/*
+ * Store from GPR register to memory
+ */
+static void do_gpr_st(DisasContext *s, TCGv_i64 source,
+ TCGv_i64 tcg_addr, int size)
+{
+ g_assert(size <= 3);
+ tcg_gen_qemu_st_i64(source, tcg_addr, get_mem_index(s), MO_TE + size);
+}
+
+/*
+ * Load from memory to GPR register
+ */
+static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr,
+ int size, bool is_signed, bool extend)
+{
+ TCGMemOp memop = MO_TE + size;
+
+ g_assert(size <= 3);
+
+ if (is_signed) {
+ memop += MO_SIGN;
+ }
+
+ tcg_gen_qemu_ld_i64(dest, tcg_addr, get_mem_index(s), memop);
+
+ if (extend && is_signed) {
+ g_assert(size < 3);
+ tcg_gen_ext32u_i64(dest, dest);
+ }
+}
+
+/*
+ * Store from FP register to memory
+ */
+static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size)
+{
+ /* This writes the bottom N bits of a 128 bit wide vector to memory */
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp, cpu_env, fp_reg_offset(srcidx, MO_64));
+ if (size < 4) {
+ tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TE + size);
+ } else {
+ TCGv_i64 tcg_hiaddr = tcg_temp_new_i64();
+ tcg_gen_qemu_st_i64(tmp, tcg_addr, get_mem_index(s), MO_TEQ);
+ tcg_gen_qemu_st64(tmp, tcg_addr, get_mem_index(s));
+ tcg_gen_ld_i64(tmp, cpu_env, fp_reg_hi_offset(srcidx));
+ tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8);
+ tcg_gen_qemu_st_i64(tmp, tcg_hiaddr, get_mem_index(s), MO_TEQ);
+ tcg_temp_free_i64(tcg_hiaddr);
+ }
+
+ tcg_temp_free_i64(tmp);
+}
+
+/*
+ * Load from memory to FP register
+ */
+static void do_fp_ld(DisasContext *s, int destidx, TCGv_i64 tcg_addr, int size)
+{
+ /* This always zero-extends and writes to a full 128 bit wide vector */
+ TCGv_i64 tmplo = tcg_temp_new_i64();
+ TCGv_i64 tmphi;
+
+ if (size < 4) {
+ TCGMemOp memop = MO_TE + size;
+ tmphi = tcg_const_i64(0);
+ tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), memop);
+ } else {
+ TCGv_i64 tcg_hiaddr;
+ tmphi = tcg_temp_new_i64();
+ tcg_hiaddr = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), MO_TEQ);
+ tcg_gen_addi_i64(tcg_hiaddr, tcg_addr, 8);
+ tcg_gen_qemu_ld_i64(tmphi, tcg_hiaddr, get_mem_index(s), MO_TEQ);
+ tcg_temp_free_i64(tcg_hiaddr);
+ }
+
+ tcg_gen_st_i64(tmplo, cpu_env, fp_reg_offset(destidx, MO_64));
+ tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(destidx));
+
+ tcg_temp_free_i64(tmplo);
+ tcg_temp_free_i64(tmphi);
+}
+
+/*
+ * This utility function is for doing register extension with an
+ * optional shift. You will likely want to pass a temporary for the
+ * destination register. See DecodeRegExtend() in the ARM ARM.
+ */
+static void ext_and_shift_reg(TCGv_i64 tcg_out, TCGv_i64 tcg_in,
+ int option, unsigned int shift)
+{
+ int extsize = extract32(option, 0, 2);
+ bool is_signed = extract32(option, 2, 1);
+
+ if (is_signed) {
+ switch (extsize) {
+ case 0:
+ tcg_gen_ext8s_i64(tcg_out, tcg_in);
+ break;
+ case 1:
+ tcg_gen_ext16s_i64(tcg_out, tcg_in);
+ break;
+ case 2:
+ tcg_gen_ext32s_i64(tcg_out, tcg_in);
+ break;
+ case 3:
+ tcg_gen_mov_i64(tcg_out, tcg_in);
+ break;
+ }
+ } else {
+ switch (extsize) {
+ case 0:
+ tcg_gen_ext8u_i64(tcg_out, tcg_in);
+ break;
+ case 1:
+ tcg_gen_ext16u_i64(tcg_out, tcg_in);
+ break;
+ case 2:
+ tcg_gen_ext32u_i64(tcg_out, tcg_in);
+ break;
+ case 3:
+ tcg_gen_mov_i64(tcg_out, tcg_in);
+ break;
+ }
+ }
+
+ if (shift) {
+ tcg_gen_shli_i64(tcg_out, tcg_out, shift);
+ }
+}
+
+static inline void gen_check_sp_alignment(DisasContext *s)
+{
+ /* The AArch64 architecture mandates that (if enabled via PSTATE
+ * or SCTLR bits) there is a check that SP is 16-aligned on every
+ * SP-relative load or store (with an exception generated if it is not).
+ * In line with general QEMU practice regarding misaligned accesses,
+ * we omit these checks for the sake of guest program performance.
+ * This function is provided as a hook so we can more easily add these
+ * checks in future (possibly as a "favour catching guest program bugs
+ * over speed" user selectable option).
+ */
+}
+
+/*
+ * the instruction disassembly implemented here matches
+ * the instruction encoding classifications in chapter 3 (C3)
+ * of the ARM Architecture Reference Manual (DDI0487A_a)
+ */
+
+/* C3.2.7 Unconditional branch (immediate)
+ * 31 30 26 25 0
+ * +----+-----------+-------------------------------------+
+ * | op | 0 0 1 0 1 | imm26 |
+ * +----+-----------+-------------------------------------+
+ */
+static void disas_uncond_b_imm(DisasContext *s, uint32_t insn)
+{
+ uint64_t addr = s->pc + sextract32(insn, 0, 26) * 4 - 4;
+
+ if (insn & (1 << 31)) {
+ /* C5.6.26 BL Branch with link */
+ tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
+ }
+
+ /* C5.6.20 B Branch / C5.6.26 BL Branch with link */
+ gen_goto_tb(s, 0, addr);
+}
+
+/* C3.2.1 Compare & branch (immediate)
+ * 31 30 25 24 23 5 4 0
+ * +----+-------------+----+---------------------+--------+
+ * | sf | 0 1 1 0 1 0 | op | imm19 | Rt |
+ * +----+-------------+----+---------------------+--------+
+ */
+static void disas_comp_b_imm(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, op, rt;
+ uint64_t addr;
+ int label_match;
+ TCGv_i64 tcg_cmp;
+
+ sf = extract32(insn, 31, 1);
+ op = extract32(insn, 24, 1); /* 0: CBZ; 1: CBNZ */
+ rt = extract32(insn, 0, 5);
+ addr = s->pc + sextract32(insn, 5, 19) * 4 - 4;
+
+ tcg_cmp = read_cpu_reg(s, rt, sf);
+ label_match = gen_new_label();
+
+ tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ,
+ tcg_cmp, 0, label_match);
+
+ gen_goto_tb(s, 0, s->pc);
+ gen_set_label(label_match);
+ gen_goto_tb(s, 1, addr);
+}
+
+/* C3.2.5 Test & branch (immediate)
+ * 31 30 25 24 23 19 18 5 4 0
+ * +----+-------------+----+-------+-------------+------+
+ * | b5 | 0 1 1 0 1 1 | op | b40 | imm14 | Rt |
+ * +----+-------------+----+-------+-------------+------+
+ */
+static void disas_test_b_imm(DisasContext *s, uint32_t insn)
+{
+ unsigned int bit_pos, op, rt;
+ uint64_t addr;
+ int label_match;
+ TCGv_i64 tcg_cmp;
+
+ bit_pos = (extract32(insn, 31, 1) << 5) | extract32(insn, 19, 5);
+ op = extract32(insn, 24, 1); /* 0: TBZ; 1: TBNZ */
+ addr = s->pc + sextract32(insn, 5, 14) * 4 - 4;
+ rt = extract32(insn, 0, 5);
+
+ tcg_cmp = tcg_temp_new_i64();
+ tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, rt), (1ULL << bit_pos));
+ label_match = gen_new_label();
+ tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ,
+ tcg_cmp, 0, label_match);
+ tcg_temp_free_i64(tcg_cmp);
+ gen_goto_tb(s, 0, s->pc);
+ gen_set_label(label_match);
+ gen_goto_tb(s, 1, addr);
+}
+
+/* C3.2.2 / C5.6.19 Conditional branch (immediate)
+ * 31 25 24 23 5 4 3 0
+ * +---------------+----+---------------------+----+------+
+ * | 0 1 0 1 0 1 0 | o1 | imm19 | o0 | cond |
+ * +---------------+----+---------------------+----+------+
+ */
+static void disas_cond_b_imm(DisasContext *s, uint32_t insn)
+{
+ unsigned int cond;
+ uint64_t addr;
+
+ if ((insn & (1 << 4)) || (insn & (1 << 24))) {
+ unallocated_encoding(s);
+ return;
+ }
+ addr = s->pc + sextract32(insn, 5, 19) * 4 - 4;
+ cond = extract32(insn, 0, 4);
+
+ if (cond < 0x0e) {
+ /* genuinely conditional branches */
+ int label_match = gen_new_label();
+ arm_gen_test_cc(cond, label_match);
+ gen_goto_tb(s, 0, s->pc);
+ gen_set_label(label_match);
+ gen_goto_tb(s, 1, addr);
+ } else {
+ /* 0xe and 0xf are both "always" conditions */
+ gen_goto_tb(s, 0, addr);
+ }
+}
+
+/* C5.6.68 HINT */
+static void handle_hint(DisasContext *s, uint32_t insn,
+ unsigned int op1, unsigned int op2, unsigned int crm)
+{
+ unsigned int selector = crm << 3 | op2;
+
+ if (op1 != 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (selector) {
+ case 0: /* NOP */
+ return;
+ case 1: /* YIELD */
+ case 2: /* WFE */
+ case 3: /* WFI */
+ case 4: /* SEV */
+ case 5: /* SEVL */
+ /* we treat all as NOP at least for now */
+ return;
+ default:
+ /* default specified as NOP equivalent */
+ return;
+ }
+}
+
+static void gen_clrex(DisasContext *s, uint32_t insn)
+{
+ tcg_gen_movi_i64(cpu_exclusive_addr, -1);
+}
+
+/* CLREX, DSB, DMB, ISB */
+static void handle_sync(DisasContext *s, uint32_t insn,
+ unsigned int op1, unsigned int op2, unsigned int crm)
+{
+ if (op1 != 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (op2) {
+ case 2: /* CLREX */
+ gen_clrex(s, insn);
+ return;
+ case 4: /* DSB */
+ case 5: /* DMB */
+ case 6: /* ISB */
+ /* We don't emulate caches so barriers are no-ops */
+ return;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+}
+
+/* C5.6.130 MSR (immediate) - move immediate to processor state field */
+static void handle_msr_i(DisasContext *s, uint32_t insn,
+ unsigned int op1, unsigned int op2, unsigned int crm)
+{
+ unsupported_encoding(s, insn);
+}
+
+static void gen_get_nzcv(TCGv_i64 tcg_rt)
+{
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ TCGv_i32 nzcv = tcg_temp_new_i32();
+
+ /* build bit 31, N */
+ tcg_gen_andi_i32(nzcv, cpu_NF, (1 << 31));
+ /* build bit 30, Z */
+ tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_ZF, 0);
+ tcg_gen_deposit_i32(nzcv, nzcv, tmp, 30, 1);
+ /* build bit 29, C */
+ tcg_gen_deposit_i32(nzcv, nzcv, cpu_CF, 29, 1);
+ /* build bit 28, V */
+ tcg_gen_shri_i32(tmp, cpu_VF, 31);
+ tcg_gen_deposit_i32(nzcv, nzcv, tmp, 28, 1);
+ /* generate result */
+ tcg_gen_extu_i32_i64(tcg_rt, nzcv);
+
+ tcg_temp_free_i32(nzcv);
+ tcg_temp_free_i32(tmp);
+}
+
+static void gen_set_nzcv(TCGv_i64 tcg_rt)
+
+{
+ TCGv_i32 nzcv = tcg_temp_new_i32();
+
+ /* take NZCV from R[t] */
+ tcg_gen_trunc_i64_i32(nzcv, tcg_rt);
+
+ /* bit 31, N */
+ tcg_gen_andi_i32(cpu_NF, nzcv, (1 << 31));
+ /* bit 30, Z */
+ tcg_gen_andi_i32(cpu_ZF, nzcv, (1 << 30));
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_ZF, cpu_ZF, 0);
+ /* bit 29, C */
+ tcg_gen_andi_i32(cpu_CF, nzcv, (1 << 29));
+ tcg_gen_shri_i32(cpu_CF, cpu_CF, 29);
+ /* bit 28, V */
+ tcg_gen_andi_i32(cpu_VF, nzcv, (1 << 28));
+ tcg_gen_shli_i32(cpu_VF, cpu_VF, 3);
+ tcg_temp_free_i32(nzcv);
+}
+
+/* C5.6.129 MRS - move from system register
+ * C5.6.131 MSR (register) - move to system register
+ * C5.6.204 SYS
+ * C5.6.205 SYSL
+ * These are all essentially the same insn in 'read' and 'write'
+ * versions, with varying op0 fields.
+ */
+static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
+ unsigned int op0, unsigned int op1, unsigned int op2,
+ unsigned int crn, unsigned int crm, unsigned int rt)
+{
+ const ARMCPRegInfo *ri;
+ TCGv_i64 tcg_rt;
+
+ ri = get_arm_cp_reginfo(s->cp_regs,
+ ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP,
+ crn, crm, op0, op1, op2));
+
+ if (!ri) {
+ /* Unknown register */
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* Check access permissions */
+ if (!cp_access_ok(s->current_pl, ri, isread)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* Handle special cases first */
+ switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
+ case ARM_CP_NOP:
+ return;
+ case ARM_CP_NZCV:
+ tcg_rt = cpu_reg(s, rt);
+ if (isread) {
+ gen_get_nzcv(tcg_rt);
+ } else {
+ gen_set_nzcv(tcg_rt);
+ }
+ return;
+ default:
+ break;
+ }
+
+ if (use_icount && (ri->type & ARM_CP_IO)) {
+ gen_io_start();
+ }
+
+ tcg_rt = cpu_reg(s, rt);
+
+ if (isread) {
+ if (ri->type & ARM_CP_CONST) {
+ tcg_gen_movi_i64(tcg_rt, ri->resetvalue);
+ } else if (ri->readfn) {
+ TCGv_ptr tmpptr;
+ gen_a64_set_pc_im(s->pc - 4);
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_get_cp_reg64(tcg_rt, cpu_env, tmpptr);
+ tcg_temp_free_ptr(tmpptr);
+ } else {
+ tcg_gen_ld_i64(tcg_rt, cpu_env, ri->fieldoffset);
+ }
+ } else {
+ if (ri->type & ARM_CP_CONST) {
+ /* If not forbidden by access permissions, treat as WI */
+ return;
+ } else if (ri->writefn) {
+ TCGv_ptr tmpptr;
+ gen_a64_set_pc_im(s->pc - 4);
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_set_cp_reg64(cpu_env, tmpptr, tcg_rt);
+ tcg_temp_free_ptr(tmpptr);
+ } else {
+ tcg_gen_st_i64(tcg_rt, cpu_env, ri->fieldoffset);
+ }
+ }
+
+ if (use_icount && (ri->type & ARM_CP_IO)) {
+ /* I/O operations must end the TB here (whether read or write) */
+ gen_io_end();
+ s->is_jmp = DISAS_UPDATE;
+ } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
+ /* We default to ending the TB on a coprocessor register write,
+ * but allow this to be suppressed by the register definition
+ * (usually only necessary to work around guest bugs).
+ */
+ s->is_jmp = DISAS_UPDATE;
+ }
+}
+
+/* C3.2.4 System
+ * 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
+ * +---------------------+---+-----+-----+-------+-------+-----+------+
+ * | 1 1 0 1 0 1 0 1 0 0 | L | op0 | op1 | CRn | CRm | op2 | Rt |
+ * +---------------------+---+-----+-----+-------+-------+-----+------+
+ */
+static void disas_system(DisasContext *s, uint32_t insn)
+{
+ unsigned int l, op0, op1, crn, crm, op2, rt;
+ l = extract32(insn, 21, 1);
+ op0 = extract32(insn, 19, 2);
+ op1 = extract32(insn, 16, 3);
+ crn = extract32(insn, 12, 4);
+ crm = extract32(insn, 8, 4);
+ op2 = extract32(insn, 5, 3);
+ rt = extract32(insn, 0, 5);
+
+ if (op0 == 0) {
+ if (l || rt != 31) {
+ unallocated_encoding(s);
+ return;
+ }
+ switch (crn) {
+ case 2: /* C5.6.68 HINT */
+ handle_hint(s, insn, op1, op2, crm);
+ break;
+ case 3: /* CLREX, DSB, DMB, ISB */
+ handle_sync(s, insn, op1, op2, crm);
+ break;
+ case 4: /* C5.6.130 MSR (immediate) */
+ handle_msr_i(s, insn, op1, op2, crm);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+ return;
+ }
+ handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt);
+}
+
+/* C3.2.3 Exception generation
+ *
+ * 31 24 23 21 20 5 4 2 1 0
+ * +-----------------+-----+------------------------+-----+----+
+ * | 1 1 0 1 0 1 0 0 | opc | imm16 | op2 | LL |
+ * +-----------------------+------------------------+----------+
+ */
+static void disas_exc(DisasContext *s, uint32_t insn)
+{
+ int opc = extract32(insn, 21, 3);
+ int op2_ll = extract32(insn, 0, 5);
+
+ switch (opc) {
+ case 0:
+ /* SVC, HVC, SMC; since we don't support the Virtualization
+ * or TrustZone extensions these all UNDEF except SVC.
+ */
+ if (op2_ll != 1) {
+ unallocated_encoding(s);
+ break;
+ }
+ gen_exception_insn(s, 0, EXCP_SWI);
+ break;
+ case 1:
+ if (op2_ll != 0) {
+ unallocated_encoding(s);
+ break;
+ }
+ /* BRK */
+ gen_exception_insn(s, 0, EXCP_BKPT);
+ break;
+ case 2:
+ if (op2_ll != 0) {
+ unallocated_encoding(s);
+ break;
+ }
+ /* HLT */
+ unsupported_encoding(s, insn);
+ break;
+ case 5:
+ if (op2_ll < 1 || op2_ll > 3) {
+ unallocated_encoding(s);
+ break;
+ }
+ /* DCPS1, DCPS2, DCPS3 */
+ unsupported_encoding(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* C3.2.7 Unconditional branch (register)
+ * 31 25 24 21 20 16 15 10 9 5 4 0
+ * +---------------+-------+-------+-------+------+-------+
+ * | 1 1 0 1 0 1 1 | opc | op2 | op3 | Rn | op4 |
+ * +---------------+-------+-------+-------+------+-------+
+ */
+static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
+{
+ unsigned int opc, op2, op3, rn, op4;
+
+ opc = extract32(insn, 21, 4);
+ op2 = extract32(insn, 16, 5);
+ op3 = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ op4 = extract32(insn, 0, 5);
+
+ if (op4 != 0x0 || op3 != 0x0 || op2 != 0x1f) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (opc) {
+ case 0: /* BR */
+ case 2: /* RET */
+ break;
+ case 1: /* BLR */
+ tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
+ break;
+ case 4: /* ERET */
+ case 5: /* DRPS */
+ if (rn != 0x1f) {
+ unallocated_encoding(s);
+ } else {
+ unsupported_encoding(s, insn);
+ }
+ return;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
+ s->is_jmp = DISAS_JUMP;
+}
+
+/* C3.2 Branches, exception generating and system instructions */
+static void disas_b_exc_sys(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 25, 7)) {
+ case 0x0a: case 0x0b:
+ case 0x4a: case 0x4b: /* Unconditional branch (immediate) */
+ disas_uncond_b_imm(s, insn);
+ break;
+ case 0x1a: case 0x5a: /* Compare & branch (immediate) */
+ disas_comp_b_imm(s, insn);
+ break;
+ case 0x1b: case 0x5b: /* Test & branch (immediate) */
+ disas_test_b_imm(s, insn);
+ break;
+ case 0x2a: /* Conditional branch (immediate) */
+ disas_cond_b_imm(s, insn);
+ break;
+ case 0x6a: /* Exception generation / System */
+ if (insn & (1 << 24)) {
+ disas_system(s, insn);
+ } else {
+ disas_exc(s, insn);
+ }
+ break;
+ case 0x6b: /* Unconditional branch (register) */
+ disas_uncond_b_reg(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/*
+ * Load/Store exclusive instructions are implemented by remembering
+ * the value/address loaded, and seeing if these are the same
+ * when the store is performed. This is not actually the architecturally
+ * mandated semantics, but it works for typical guest code sequences
+ * and avoids having to monitor regular stores.
+ *
+ * In system emulation mode only one CPU will be running at once, so
+ * this sequence is effectively atomic. In user emulation mode we
+ * throw an exception and handle the atomic operation elsewhere.
+ */
+static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
+ TCGv_i64 addr, int size, bool is_pair)
+{
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ TCGMemOp memop = MO_TE + size;
+
+ g_assert(size <= 3);
+ tcg_gen_qemu_ld_i64(tmp, addr, get_mem_index(s), memop);
+
+ if (is_pair) {
+ TCGv_i64 addr2 = tcg_temp_new_i64();
+ TCGv_i64 hitmp = tcg_temp_new_i64();
+
+ g_assert(size >= 2);
+ tcg_gen_addi_i64(addr2, addr, 1 << size);
+ tcg_gen_qemu_ld_i64(hitmp, addr2, get_mem_index(s), memop);
+ tcg_temp_free_i64(addr2);
+ tcg_gen_mov_i64(cpu_exclusive_high, hitmp);
+ tcg_gen_mov_i64(cpu_reg(s, rt2), hitmp);
+ tcg_temp_free_i64(hitmp);
+ }
+
+ tcg_gen_mov_i64(cpu_exclusive_val, tmp);
+ tcg_gen_mov_i64(cpu_reg(s, rt), tmp);
+
+ tcg_temp_free_i64(tmp);
+ tcg_gen_mov_i64(cpu_exclusive_addr, addr);
+}
+
+#ifdef CONFIG_USER_ONLY
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv_i64 addr, int size, int is_pair)
+{
+ tcg_gen_mov_i64(cpu_exclusive_test, addr);
+ tcg_gen_movi_i32(cpu_exclusive_info,
+ size | is_pair << 2 | (rd << 4) | (rt << 9) | (rt2 << 14));
+ gen_exception_insn(s, 4, EXCP_STREX);
+}
+#else
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv_i64 addr, int size, int is_pair)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "%s:%d: system mode store_exclusive unsupported "
+ "at pc=%016" PRIx64 "\n",
+ __FILE__, __LINE__, s->pc - 4);
+}
+#endif
+
+/* C3.3.6 Load/store exclusive
+ *
+ * 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0
+ * +-----+-------------+----+---+----+------+----+-------+------+------+
+ * | sz | 0 0 1 0 0 0 | o2 | L | o1 | Rs | o0 | Rt2 | Rn | Rt |
+ * +-----+-------------+----+---+----+------+----+-------+------+------+
+ *
+ * sz: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64 bit
+ * L: 0 -> store, 1 -> load
+ * o2: 0 -> exclusive, 1 -> not
+ * o1: 0 -> single register, 1 -> register pair
+ * o0: 1 -> load-acquire/store-release, 0 -> not
+ *
+ * o0 == 0 AND o2 == 1 is un-allocated
+ * o1 == 1 is un-allocated except for 32 and 64 bit sizes
+ */
+static void disas_ldst_excl(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int rt2 = extract32(insn, 10, 5);
+ int is_lasr = extract32(insn, 15, 1);
+ int rs = extract32(insn, 16, 5);
+ int is_pair = extract32(insn, 21, 1);
+ int is_store = !extract32(insn, 22, 1);
+ int is_excl = !extract32(insn, 23, 1);
+ int size = extract32(insn, 30, 2);
+ TCGv_i64 tcg_addr;
+
+ if ((!is_excl && !is_lasr) ||
+ (is_pair && size < 2)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+
+ /* Note that since TCG is single threaded load-acquire/store-release
+ * semantics require no extra if (is_lasr) { ... } handling.
+ */
+
+ if (is_excl) {
+ if (!is_store) {
+ gen_load_exclusive(s, rt, rt2, tcg_addr, size, is_pair);
+ } else {
+ gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, is_pair);
+ }
+ } else {
+ TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ if (is_store) {
+ do_gpr_st(s, tcg_rt, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false);
+ }
+ if (is_pair) {
+ TCGv_i64 tcg_rt2 = cpu_reg(s, rt);
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size);
+ if (is_store) {
+ do_gpr_st(s, tcg_rt2, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt2, tcg_addr, size, false, false);
+ }
+ }
+ }
+}
+
+/*
+ * C3.3.5 Load register (literal)
+ *
+ * 31 30 29 27 26 25 24 23 5 4 0
+ * +-----+-------+---+-----+-------------------+-------+
+ * | opc | 0 1 1 | V | 0 0 | imm19 | Rt |
+ * +-----+-------+---+-----+-------------------+-------+
+ *
+ * V: 1 -> vector (simd/fp)
+ * opc (non-vector): 00 -> 32 bit, 01 -> 64 bit,
+ * 10-> 32 bit signed, 11 -> prefetch
+ * opc (vector): 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit (11 unallocated)
+ */
+static void disas_ld_lit(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int64_t imm = sextract32(insn, 5, 19) << 2;
+ bool is_vector = extract32(insn, 26, 1);
+ int opc = extract32(insn, 30, 2);
+ bool is_signed = false;
+ int size = 2;
+ TCGv_i64 tcg_rt, tcg_addr;
+
+ if (is_vector) {
+ if (opc == 3) {
+ unallocated_encoding(s);
+ return;
+ }
+ size = 2 + opc;
+ } else {
+ if (opc == 3) {
+ /* PRFM (literal) : prefetch */
+ return;
+ }
+ size = 2 + extract32(opc, 0, 1);
+ is_signed = extract32(opc, 1, 1);
+ }
+
+ tcg_rt = cpu_reg(s, rt);
+
+ tcg_addr = tcg_const_i64((s->pc - 4) + imm);
+ if (is_vector) {
+ do_fp_ld(s, rt, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+ }
+ tcg_temp_free_i64(tcg_addr);
+}
+
+/*
+ * C5.6.80 LDNP (Load Pair - non-temporal hint)
+ * C5.6.81 LDP (Load Pair - non vector)
+ * C5.6.82 LDPSW (Load Pair Signed Word - non vector)
+ * C5.6.176 STNP (Store Pair - non-temporal hint)
+ * C5.6.177 STP (Store Pair - non vector)
+ * C6.3.165 LDNP (Load Pair of SIMD&FP - non-temporal hint)
+ * C6.3.165 LDP (Load Pair of SIMD&FP)
+ * C6.3.284 STNP (Store Pair of SIMD&FP - non-temporal hint)
+ * C6.3.284 STP (Store Pair of SIMD&FP)
+ *
+ * 31 30 29 27 26 25 24 23 22 21 15 14 10 9 5 4 0
+ * +-----+-------+---+---+-------+---+-----------------------------+
+ * | opc | 1 0 1 | V | 0 | index | L | imm7 | Rt2 | Rn | Rt |
+ * +-----+-------+---+---+-------+---+-------+-------+------+------+
+ *
+ * opc: LDP/STP/LDNP/STNP 00 -> 32 bit, 10 -> 64 bit
+ * LDPSW 01
+ * LDP/STP/LDNP/STNP (SIMD) 00 -> 32 bit, 01 -> 64 bit, 10 -> 128 bit
+ * V: 0 -> GPR, 1 -> Vector
+ * idx: 00 -> signed offset with non-temporal hint, 01 -> post-index,
+ * 10 -> signed offset, 11 -> pre-index
+ * L: 0 -> Store 1 -> Load
+ *
+ * Rt, Rt2 = GPR or SIMD registers to be stored
+ * Rn = general purpose register containing address
+ * imm7 = signed offset (multiple of 4 or 8 depending on size)
+ */
+static void disas_ldst_pair(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int rt2 = extract32(insn, 10, 5);
+ int64_t offset = sextract32(insn, 15, 7);
+ int index = extract32(insn, 23, 2);
+ bool is_vector = extract32(insn, 26, 1);
+ bool is_load = extract32(insn, 22, 1);
+ int opc = extract32(insn, 30, 2);
+
+ bool is_signed = false;
+ bool postindex = false;
+ bool wback = false;
+
+ TCGv_i64 tcg_addr; /* calculated address */
+ int size;
+
+ if (opc == 3) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (is_vector) {
+ size = 2 + opc;
+ } else {
+ size = 2 + extract32(opc, 1, 1);
+ is_signed = extract32(opc, 0, 1);
+ if (!is_load && is_signed) {
+ unallocated_encoding(s);
+ return;
+ }
+ }
+
+ switch (index) {
+ case 1: /* post-index */
+ postindex = true;
+ wback = true;
+ break;
+ case 0:
+ /* signed offset with "non-temporal" hint. Since we don't emulate
+ * caches we don't care about hints to the cache system about
+ * data access patterns, and handle this identically to plain
+ * signed offset.
+ */
+ if (is_signed) {
+ /* There is no non-temporal-hint version of LDPSW */
+ unallocated_encoding(s);
+ return;
+ }
+ postindex = false;
+ break;
+ case 2: /* signed offset, rn not updated */
+ postindex = false;
+ break;
+ case 3: /* pre-index */
+ postindex = false;
+ wback = true;
+ break;
+ }
+
+ offset <<= size;
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+
+ if (!postindex) {
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, offset);
+ }
+
+ if (is_vector) {
+ if (is_load) {
+ do_fp_ld(s, rt, tcg_addr, size);
+ } else {
+ do_fp_st(s, rt, tcg_addr, size);
+ }
+ } else {
+ TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ if (is_load) {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false);
+ } else {
+ do_gpr_st(s, tcg_rt, tcg_addr, size);
+ }
+ }
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size);
+ if (is_vector) {
+ if (is_load) {
+ do_fp_ld(s, rt2, tcg_addr, size);
+ } else {
+ do_fp_st(s, rt2, tcg_addr, size);
+ }
+ } else {
+ TCGv_i64 tcg_rt2 = cpu_reg(s, rt2);
+ if (is_load) {
+ do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false);
+ } else {
+ do_gpr_st(s, tcg_rt2, tcg_addr, size);
+ }
+ }
+
+ if (wback) {
+ if (postindex) {
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, offset - (1 << size));
+ } else {
+ tcg_gen_subi_i64(tcg_addr, tcg_addr, 1 << size);
+ }
+ tcg_gen_mov_i64(cpu_reg_sp(s, rn), tcg_addr);
+ }
+}
+
+/*
+ * C3.3.8 Load/store (immediate post-indexed)
+ * C3.3.9 Load/store (immediate pre-indexed)
+ * C3.3.12 Load/store (unscaled immediate)
+ *
+ * 31 30 29 27 26 25 24 23 22 21 20 12 11 10 9 5 4 0
+ * +----+-------+---+-----+-----+---+--------+-----+------+------+
+ * |size| 1 1 1 | V | 0 0 | opc | 0 | imm9 | idx | Rn | Rt |
+ * +----+-------+---+-----+-----+---+--------+-----+------+------+
+ *
+ * idx = 01 -> post-indexed, 11 pre-indexed, 00 unscaled imm. (no writeback)
+ * V = 0 -> non-vector
+ * size: 00 -> 8 bit, 01 -> 16 bit, 10 -> 32 bit, 11 -> 64bit
+ * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32
+ */
+static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int imm9 = sextract32(insn, 12, 9);
+ int opc = extract32(insn, 22, 2);
+ int size = extract32(insn, 30, 2);
+ int idx = extract32(insn, 10, 2);
+ bool is_signed = false;
+ bool is_store = false;
+ bool is_extended = false;
+ bool is_vector = extract32(insn, 26, 1);
+ bool post_index;
+ bool writeback;
+
+ TCGv_i64 tcg_addr;
+
+ if (is_vector) {
+ size |= (opc & 2) << 1;
+ if (size > 4) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = ((opc & 1) == 0);
+ } else {
+ if (size == 3 && opc == 2) {
+ /* PRFM - prefetch */
+ return;
+ }
+ if (opc == 3 && size > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = (opc == 0);
+ is_signed = opc & (1<<1);
+ is_extended = (size < 3) && (opc & 1);
+ }
+
+ switch (idx) {
+ case 0:
+ post_index = false;
+ writeback = false;
+ break;
+ case 1:
+ post_index = true;
+ writeback = true;
+ break;
+ case 3:
+ post_index = false;
+ writeback = true;
+ break;
+ case 2:
+ g_assert(false);
+ break;
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+
+ if (!post_index) {
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, imm9);
+ }
+
+ if (is_vector) {
+ if (is_store) {
+ do_fp_st(s, rt, tcg_addr, size);
+ } else {
+ do_fp_ld(s, rt, tcg_addr, size);
+ }
+ } else {
+ TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ if (is_store) {
+ do_gpr_st(s, tcg_rt, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+ }
+ }
+
+ if (writeback) {
+ TCGv_i64 tcg_rn = cpu_reg_sp(s, rn);
+ if (post_index) {
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, imm9);
+ }
+ tcg_gen_mov_i64(tcg_rn, tcg_addr);
+ }
+}
+
+/*
+ * C3.3.10 Load/store (register offset)
+ *
+ * 31 30 29 27 26 25 24 23 22 21 20 16 15 13 12 11 10 9 5 4 0
+ * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+
+ * |size| 1 1 1 | V | 0 0 | opc | 1 | Rm | opt | S| 1 0 | Rn | Rt |
+ * +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+
+ *
+ * For non-vector:
+ * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit
+ * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32
+ * For vector:
+ * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated
+ * opc<0>: 0 -> store, 1 -> load
+ * V: 1 -> vector/simd
+ * opt: extend encoding (see DecodeRegExtend)
+ * S: if S=1 then scale (essentially index by sizeof(size))
+ * Rt: register to transfer into/out of
+ * Rn: address register or SP for base
+ * Rm: offset register or ZR for offset
+ */
+static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int shift = extract32(insn, 12, 1);
+ int rm = extract32(insn, 16, 5);
+ int opc = extract32(insn, 22, 2);
+ int opt = extract32(insn, 13, 3);
+ int size = extract32(insn, 30, 2);
+ bool is_signed = false;
+ bool is_store = false;
+ bool is_extended = false;
+ bool is_vector = extract32(insn, 26, 1);
+
+ TCGv_i64 tcg_rm;
+ TCGv_i64 tcg_addr;
+
+ if (extract32(opt, 1, 1) == 0) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (is_vector) {
+ size |= (opc & 2) << 1;
+ if (size > 4) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = !extract32(opc, 0, 1);
+ } else {
+ if (size == 3 && opc == 2) {
+ /* PRFM - prefetch */
+ return;
+ }
+ if (opc == 3 && size > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = (opc == 0);
+ is_signed = extract32(opc, 1, 1);
+ is_extended = (size < 3) && extract32(opc, 0, 1);
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+
+ tcg_rm = read_cpu_reg(s, rm, 1);
+ ext_and_shift_reg(tcg_rm, tcg_rm, opt, shift ? size : 0);
+
+ tcg_gen_add_i64(tcg_addr, tcg_addr, tcg_rm);
+
+ if (is_vector) {
+ if (is_store) {
+ do_fp_st(s, rt, tcg_addr, size);
+ } else {
+ do_fp_ld(s, rt, tcg_addr, size);
+ }
+ } else {
+ TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ if (is_store) {
+ do_gpr_st(s, tcg_rt, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+ }
+ }
+}
+
+/*
+ * C3.3.13 Load/store (unsigned immediate)
+ *
+ * 31 30 29 27 26 25 24 23 22 21 10 9 5
+ * +----+-------+---+-----+-----+------------+-------+------+
+ * |size| 1 1 1 | V | 0 1 | opc | imm12 | Rn | Rt |
+ * +----+-------+---+-----+-----+------------+-------+------+
+ *
+ * For non-vector:
+ * size: 00-> byte, 01 -> 16 bit, 10 -> 32bit, 11 -> 64bit
+ * opc: 00 -> store, 01 -> loadu, 10 -> loads 64, 11 -> loads 32
+ * For vector:
+ * size is opc<1>:size<1:0> so 100 -> 128 bit; 110 and 111 unallocated
+ * opc<0>: 0 -> store, 1 -> load
+ * Rn: base address register (inc SP)
+ * Rt: target register
+ */
+static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn)
+{
+ int rt = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ unsigned int imm12 = extract32(insn, 10, 12);
+ bool is_vector = extract32(insn, 26, 1);
+ int size = extract32(insn, 30, 2);
+ int opc = extract32(insn, 22, 2);
+ unsigned int offset;
+
+ TCGv_i64 tcg_addr;
+
+ bool is_store;
+ bool is_signed = false;
+ bool is_extended = false;
+
+ if (is_vector) {
+ size |= (opc & 2) << 1;
+ if (size > 4) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = !extract32(opc, 0, 1);
+ } else {
+ if (size == 3 && opc == 2) {
+ /* PRFM - prefetch */
+ return;
+ }
+ if (opc == 3 && size > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+ is_store = (opc == 0);
+ is_signed = extract32(opc, 1, 1);
+ is_extended = (size < 3) && extract32(opc, 0, 1);
+ }
+
+ if (rn == 31) {
+ gen_check_sp_alignment(s);
+ }
+ tcg_addr = read_cpu_reg_sp(s, rn, 1);
+ offset = imm12 << size;
+ tcg_gen_addi_i64(tcg_addr, tcg_addr, offset);
+
+ if (is_vector) {
+ if (is_store) {
+ do_fp_st(s, rt, tcg_addr, size);
+ } else {
+ do_fp_ld(s, rt, tcg_addr, size);
+ }
+ } else {
+ TCGv_i64 tcg_rt = cpu_reg(s, rt);
+ if (is_store) {
+ do_gpr_st(s, tcg_rt, tcg_addr, size);
+ } else {
+ do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended);
+ }
+ }
+}
+
+/* Load/store register (immediate forms) */
+static void disas_ldst_reg_imm(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 10, 2)) {
+ case 0: case 1: case 3:
+ /* Load/store register (unscaled immediate) */
+ /* Load/store immediate pre/post-indexed */
+ disas_ldst_reg_imm9(s, insn);
+ break;
+ case 2:
+ /* Load/store register unprivileged */
+ unsupported_encoding(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* Load/store register (all forms) */
+static void disas_ldst_reg(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 24, 2)) {
+ case 0:
+ if (extract32(insn, 21, 1) == 1 && extract32(insn, 10, 2) == 2) {
+ disas_ldst_reg_roffset(s, insn);
+ } else {
+ disas_ldst_reg_imm(s, insn);
+ }
+ break;
+ case 1:
+ disas_ldst_reg_unsigned_imm(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* AdvSIMD load/store multiple structures */
+static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* AdvSIMD load/store single structure */
+static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
+{
+ unsupported_encoding(s, insn);
+}
+
+/* C3.3 Loads and stores */
+static void disas_ldst(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 24, 6)) {
+ case 0x08: /* Load/store exclusive */
+ disas_ldst_excl(s, insn);
+ break;
+ case 0x18: case 0x1c: /* Load register (literal) */
+ disas_ld_lit(s, insn);
+ break;
+ case 0x28: case 0x29:
+ case 0x2c: case 0x2d: /* Load/store pair (all forms) */
+ disas_ldst_pair(s, insn);
+ break;
+ case 0x38: case 0x39:
+ case 0x3c: case 0x3d: /* Load/store register (all forms) */
+ disas_ldst_reg(s, insn);
+ break;
+ case 0x0c: /* AdvSIMD load/store multiple structures */
+ disas_ldst_multiple_struct(s, insn);
+ break;
+ case 0x0d: /* AdvSIMD load/store single structure */
+ disas_ldst_single_struct(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* C3.4.6 PC-rel. addressing
+ * 31 30 29 28 24 23 5 4 0
+ * +----+-------+-----------+-------------------+------+
+ * | op | immlo | 1 0 0 0 0 | immhi | Rd |
+ * +----+-------+-----------+-------------------+------+
+ */
+static void disas_pc_rel_adr(DisasContext *s, uint32_t insn)
+{
+ unsigned int page, rd;
+ uint64_t base;
+ int64_t offset;
+
+ page = extract32(insn, 31, 1);
+ /* SignExtend(immhi:immlo) -> offset */
+ offset = ((int64_t)sextract32(insn, 5, 19) << 2) | extract32(insn, 29, 2);
+ rd = extract32(insn, 0, 5);
+ base = s->pc - 4;
+
+ if (page) {
+ /* ADRP (page based) */
+ base &= ~0xfff;
+ offset <<= 12;
+ }
+
+ tcg_gen_movi_i64(cpu_reg(s, rd), base + offset);
+}
+
+/*
+ * C3.4.1 Add/subtract (immediate)
+ *
+ * 31 30 29 28 24 23 22 21 10 9 5 4 0
+ * +--+--+--+-----------+-----+-------------+-----+-----+
+ * |sf|op| S| 1 0 0 0 1 |shift| imm12 | Rn | Rd |
+ * +--+--+--+-----------+-----+-------------+-----+-----+
+ *
+ * sf: 0 -> 32bit, 1 -> 64bit
+ * op: 0 -> add , 1 -> sub
+ * S: 1 -> set flags
+ * shift: 00 -> LSL imm by 0, 01 -> LSL imm by 12
+ */
+static void disas_add_sub_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ uint64_t imm = extract32(insn, 10, 12);
+ int shift = extract32(insn, 22, 2);
+ bool setflags = extract32(insn, 29, 1);
+ bool sub_op = extract32(insn, 30, 1);
+ bool is_64bit = extract32(insn, 31, 1);
+
+ TCGv_i64 tcg_rn = cpu_reg_sp(s, rn);
+ TCGv_i64 tcg_rd = setflags ? cpu_reg(s, rd) : cpu_reg_sp(s, rd);
+ TCGv_i64 tcg_result;
+
+ switch (shift) {
+ case 0x0:
+ break;
+ case 0x1:
+ imm <<= 12;
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_result = tcg_temp_new_i64();
+ if (!setflags) {
+ if (sub_op) {
+ tcg_gen_subi_i64(tcg_result, tcg_rn, imm);
+ } else {
+ tcg_gen_addi_i64(tcg_result, tcg_rn, imm);
+ }
+ } else {
+ TCGv_i64 tcg_imm = tcg_const_i64(imm);
+ if (sub_op) {
+ gen_sub_CC(is_64bit, tcg_result, tcg_rn, tcg_imm);
+ } else {
+ gen_add_CC(is_64bit, tcg_result, tcg_rn, tcg_imm);
+ }
+ tcg_temp_free_i64(tcg_imm);
+ }
+
+ if (is_64bit) {
+ tcg_gen_mov_i64(tcg_rd, tcg_result);
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_result);
+ }
+
+ tcg_temp_free_i64(tcg_result);
+}
+
+/* The input should be a value in the bottom e bits (with higher
+ * bits zero); returns that value replicated into every element
+ * of size e in a 64 bit integer.
+ */
+static uint64_t bitfield_replicate(uint64_t mask, unsigned int e)
+{
+ assert(e != 0);
+ while (e < 64) {
+ mask |= mask << e;
+ e *= 2;
+ }
+ return mask;
+}
+
+/* Return a value with the bottom len bits set (where 0 < len <= 64) */
+static inline uint64_t bitmask64(unsigned int length)
+{
+ assert(length > 0 && length <= 64);
+ return ~0ULL >> (64 - length);
+}
+
+/* Simplified variant of pseudocode DecodeBitMasks() for the case where we
+ * only require the wmask. Returns false if the imms/immr/immn are a reserved
+ * value (ie should cause a guest UNDEF exception), and true if they are
+ * valid, in which case the decoded bit pattern is written to result.
+ */
+static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn,
+ unsigned int imms, unsigned int immr)
+{
+ uint64_t mask;
+ unsigned e, levels, s, r;
+ int len;
+
+ assert(immn < 2 && imms < 64 && immr < 64);
+
+ /* The bit patterns we create here are 64 bit patterns which
+ * are vectors of identical elements of size e = 2, 4, 8, 16, 32 or
+ * 64 bits each. Each element contains the same value: a run
+ * of between 1 and e-1 non-zero bits, rotated within the
+ * element by between 0 and e-1 bits.
+ *
+ * The element size and run length are encoded into immn (1 bit)
+ * and imms (6 bits) as follows:
+ * 64 bit elements: immn = 1, imms = <length of run - 1>
+ * 32 bit elements: immn = 0, imms = 0 : <length of run - 1>
+ * 16 bit elements: immn = 0, imms = 10 : <length of run - 1>
+ * 8 bit elements: immn = 0, imms = 110 : <length of run - 1>
+ * 4 bit elements: immn = 0, imms = 1110 : <length of run - 1>
+ * 2 bit elements: immn = 0, imms = 11110 : <length of run - 1>
+ * Notice that immn = 0, imms = 11111x is the only combination
+ * not covered by one of the above options; this is reserved.
+ * Further, <length of run - 1> all-ones is a reserved pattern.
+ *
+ * In all cases the rotation is by immr % e (and immr is 6 bits).
+ */
+
+ /* First determine the element size */
+ len = 31 - clz32((immn << 6) | (~imms & 0x3f));
+ if (len < 1) {
+ /* This is the immn == 0, imms == 0x11111x case */
+ return false;
+ }
+ e = 1 << len;
+
+ levels = e - 1;
+ s = imms & levels;
+ r = immr & levels;
+
+ if (s == levels) {
+ /* <length of run - 1> mustn't be all-ones. */
+ return false;
+ }
+
+ /* Create the value of one element: s+1 set bits rotated
+ * by r within the element (which is e bits wide)...
+ */
+ mask = bitmask64(s + 1);
+ mask = (mask >> r) | (mask << (e - r));
+ /* ...then replicate the element over the whole 64 bit value */
+ mask = bitfield_replicate(mask, e);
+ *result = mask;
+ return true;
+}
+
+/* C3.4.4 Logical (immediate)
+ * 31 30 29 28 23 22 21 16 15 10 9 5 4 0
+ * +----+-----+-------------+---+------+------+------+------+
+ * | sf | opc | 1 0 0 1 0 0 | N | immr | imms | Rn | Rd |
+ * +----+-----+-------------+---+------+------+------+------+
+ */
+static void disas_logic_imm(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, opc, is_n, immr, imms, rn, rd;
+ TCGv_i64 tcg_rd, tcg_rn;
+ uint64_t wmask;
+ bool is_and = false;
+
+ sf = extract32(insn, 31, 1);
+ opc = extract32(insn, 29, 2);
+ is_n = extract32(insn, 22, 1);
+ immr = extract32(insn, 16, 6);
+ imms = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ if (!sf && is_n) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (opc == 0x3) { /* ANDS */
+ tcg_rd = cpu_reg(s, rd);
+ } else {
+ tcg_rd = cpu_reg_sp(s, rd);
+ }
+ tcg_rn = cpu_reg(s, rn);
+
+ if (!logic_imm_decode_wmask(&wmask, is_n, imms, immr)) {
+ /* some immediate field values are reserved */
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (!sf) {
+ wmask &= 0xffffffff;
+ }
+
+ switch (opc) {
+ case 0x3: /* ANDS */
+ case 0x0: /* AND */
+ tcg_gen_andi_i64(tcg_rd, tcg_rn, wmask);
+ is_and = true;
+ break;
+ case 0x1: /* ORR */
+ tcg_gen_ori_i64(tcg_rd, tcg_rn, wmask);
+ break;
+ case 0x2: /* EOR */
+ tcg_gen_xori_i64(tcg_rd, tcg_rn, wmask);
+ break;
+ default:
+ assert(FALSE); /* must handle all above */
+ break;
+ }
+
+ if (!sf && !is_and) {
+ /* zero extend final result; we know we can skip this for AND
+ * since the immediate had the high 32 bits clear.
+ */
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+
+ if (opc == 3) { /* ANDS */
+ gen_logic_CC(sf, tcg_rd);
+ }
+}
+
+/*
+ * C3.4.5 Move wide (immediate)
+ *
+ * 31 30 29 28 23 22 21 20 5 4 0
+ * +--+-----+-------------+-----+----------------+------+
+ * |sf| opc | 1 0 0 1 0 1 | hw | imm16 | Rd |
+ * +--+-----+-------------+-----+----------------+------+
+ *
+ * sf: 0 -> 32 bit, 1 -> 64 bit
+ * opc: 00 -> N, 10 -> Z, 11 -> K
+ * hw: shift/16 (0,16, and sf only 32, 48)
+ */
+static void disas_movw_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ uint64_t imm = extract32(insn, 5, 16);
+ int sf = extract32(insn, 31, 1);
+ int opc = extract32(insn, 29, 2);
+ int pos = extract32(insn, 21, 2) << 4;
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+ TCGv_i64 tcg_imm;
+
+ if (!sf && (pos >= 32)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (opc) {
+ case 0: /* MOVN */
+ case 2: /* MOVZ */
+ imm <<= pos;
+ if (opc == 0) {
+ imm = ~imm;
+ }
+ if (!sf) {
+ imm &= 0xffffffffu;
+ }
+ tcg_gen_movi_i64(tcg_rd, imm);
+ break;
+ case 3: /* MOVK */
+ tcg_imm = tcg_const_i64(imm);
+ tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_imm, pos, 16);
+ tcg_temp_free_i64(tcg_imm);
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* C3.4.2 Bitfield
+ * 31 30 29 28 23 22 21 16 15 10 9 5 4 0
+ * +----+-----+-------------+---+------+------+------+------+
+ * | sf | opc | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd |
+ * +----+-----+-------------+---+------+------+------+------+
+ */
+static void disas_bitfield(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, n, opc, ri, si, rn, rd, bitsize, pos, len;
+ TCGv_i64 tcg_rd, tcg_tmp;
+
+ sf = extract32(insn, 31, 1);
+ opc = extract32(insn, 29, 2);
+ n = extract32(insn, 22, 1);
+ ri = extract32(insn, 16, 6);
+ si = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+ bitsize = sf ? 64 : 32;
+
+ if (sf != n || ri >= bitsize || si >= bitsize || opc > 2) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rd = cpu_reg(s, rd);
+ tcg_tmp = read_cpu_reg(s, rn, sf);
+
+ /* OPTME: probably worth recognizing common cases of ext{8,16,32}{u,s} */
+
+ if (opc != 1) { /* SBFM or UBFM */
+ tcg_gen_movi_i64(tcg_rd, 0);
+ }
+
+ /* do the bit move operation */
+ if (si >= ri) {
+ /* Wd<s-r:0> = Wn<s:r> */
+ tcg_gen_shri_i64(tcg_tmp, tcg_tmp, ri);
+ pos = 0;
+ len = (si - ri) + 1;
+ } else {
+ /* Wd<32+s-r,32-r> = Wn<s:0> */
+ pos = bitsize - ri;
+ len = si + 1;
+ }
+
+ tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, pos, len);
+
+ if (opc == 0) { /* SBFM - sign extend the destination field */
+ tcg_gen_shli_i64(tcg_rd, tcg_rd, 64 - (pos + len));
+ tcg_gen_sari_i64(tcg_rd, tcg_rd, 64 - (pos + len));
+ }
+
+ if (!sf) { /* zero extend final result */
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+}
+
+/* C3.4.3 Extract
+ * 31 30 29 28 23 22 21 20 16 15 10 9 5 4 0
+ * +----+------+-------------+---+----+------+--------+------+------+
+ * | sf | op21 | 1 0 0 1 1 1 | N | o0 | Rm | imms | Rn | Rd |
+ * +----+------+-------------+---+----+------+--------+------+------+
+ */
+static void disas_extract(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, n, rm, imm, rn, rd, bitsize, op21, op0;
+
+ sf = extract32(insn, 31, 1);
+ n = extract32(insn, 22, 1);
+ rm = extract32(insn, 16, 5);
+ imm = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+ op21 = extract32(insn, 29, 2);
+ op0 = extract32(insn, 21, 1);
+ bitsize = sf ? 64 : 32;
+
+ if (sf != n || op21 || op0 || imm >= bitsize) {
+ unallocated_encoding(s);
+ } else {
+ TCGv_i64 tcg_rd, tcg_rm, tcg_rn;
+
+ tcg_rd = cpu_reg(s, rd);
+
+ if (imm) {
+ /* OPTME: we can special case rm==rn as a rotate */
+ tcg_rm = read_cpu_reg(s, rm, sf);
+ tcg_rn = read_cpu_reg(s, rn, sf);
+ tcg_gen_shri_i64(tcg_rm, tcg_rm, imm);
+ tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm);
+ tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn);
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+ } else {
+ /* tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts,
+ * so an extract from bit 0 is a special case.
+ */
+ if (sf) {
+ tcg_gen_mov_i64(tcg_rd, cpu_reg(s, rm));
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rm));
+ }
+ }
+
+ }
+}
+
+/* C3.4 Data processing - immediate */
+static void disas_data_proc_imm(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 23, 6)) {
+ case 0x20: case 0x21: /* PC-rel. addressing */
+ disas_pc_rel_adr(s, insn);
+ break;
+ case 0x22: case 0x23: /* Add/subtract (immediate) */
+ disas_add_sub_imm(s, insn);
+ break;
+ case 0x24: /* Logical (immediate) */
+ disas_logic_imm(s, insn);
+ break;
+ case 0x25: /* Move wide (immediate) */
+ disas_movw_imm(s, insn);
+ break;
+ case 0x26: /* Bitfield */
+ disas_bitfield(s, insn);
+ break;
+ case 0x27: /* Extract */
+ disas_extract(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* Shift a TCGv src by TCGv shift_amount, put result in dst.
+ * Note that it is the caller's responsibility to ensure that the
+ * shift amount is in range (ie 0..31 or 0..63) and provide the ARM
+ * mandated semantics for out of range shifts.
+ */
+static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf,
+ enum a64_shift_type shift_type, TCGv_i64 shift_amount)
+{
+ switch (shift_type) {
+ case A64_SHIFT_TYPE_LSL:
+ tcg_gen_shl_i64(dst, src, shift_amount);
+ break;
+ case A64_SHIFT_TYPE_LSR:
+ tcg_gen_shr_i64(dst, src, shift_amount);
+ break;
+ case A64_SHIFT_TYPE_ASR:
+ if (!sf) {
+ tcg_gen_ext32s_i64(dst, src);
+ }
+ tcg_gen_sar_i64(dst, sf ? src : dst, shift_amount);
+ break;
+ case A64_SHIFT_TYPE_ROR:
+ if (sf) {
+ tcg_gen_rotr_i64(dst, src, shift_amount);
+ } else {
+ TCGv_i32 t0, t1;
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(t0, src);
+ tcg_gen_trunc_i64_i32(t1, shift_amount);
+ tcg_gen_rotr_i32(t0, t0, t1);
+ tcg_gen_extu_i32_i64(dst, t0);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+ }
+ break;
+ default:
+ assert(FALSE); /* all shift types should be handled */
+ break;
+ }
+
+ if (!sf) { /* zero extend final result */
+ tcg_gen_ext32u_i64(dst, dst);
+ }
+}
+
+/* Shift a TCGv src by immediate, put result in dst.
+ * The shift amount must be in range (this should always be true as the
+ * relevant instructions will UNDEF on bad shift immediates).
+ */
+static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf,
+ enum a64_shift_type shift_type, unsigned int shift_i)
+{
+ assert(shift_i < (sf ? 64 : 32));
+
+ if (shift_i == 0) {
+ tcg_gen_mov_i64(dst, src);
+ } else {
+ TCGv_i64 shift_const;
+
+ shift_const = tcg_const_i64(shift_i);
+ shift_reg(dst, src, sf, shift_type, shift_const);
+ tcg_temp_free_i64(shift_const);
+ }
+}
+
+/* C3.5.10 Logical (shifted register)
+ * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0
+ * +----+-----+-----------+-------+---+------+--------+------+------+
+ * | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd |
+ * +----+-----+-----------+-------+---+------+--------+------+------+
+ */
+static void disas_logic_reg(DisasContext *s, uint32_t insn)
+{
+ TCGv_i64 tcg_rd, tcg_rn, tcg_rm;
+ unsigned int sf, opc, shift_type, invert, rm, shift_amount, rn, rd;
+
+ sf = extract32(insn, 31, 1);
+ opc = extract32(insn, 29, 2);
+ shift_type = extract32(insn, 22, 2);
+ invert = extract32(insn, 21, 1);
+ rm = extract32(insn, 16, 5);
+ shift_amount = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ if (!sf && (shift_amount & (1 << 5))) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rd = cpu_reg(s, rd);
+
+ if (opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31) {
+ /* Unshifted ORR and ORN with WZR/XZR is the standard encoding for
+ * register-register MOV and MVN, so it is worth special casing.
+ */
+ tcg_rm = cpu_reg(s, rm);
+ if (invert) {
+ tcg_gen_not_i64(tcg_rd, tcg_rm);
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+ } else {
+ if (sf) {
+ tcg_gen_mov_i64(tcg_rd, tcg_rm);
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rm);
+ }
+ }
+ return;
+ }
+
+ tcg_rm = read_cpu_reg(s, rm, sf);
+
+ if (shift_amount) {
+ shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount);
+ }
+
+ tcg_rn = cpu_reg(s, rn);
+
+ switch (opc | (invert << 2)) {
+ case 0: /* AND */
+ case 3: /* ANDS */
+ tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ case 1: /* ORR */
+ tcg_gen_or_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ case 2: /* EOR */
+ tcg_gen_xor_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ case 4: /* BIC */
+ case 7: /* BICS */
+ tcg_gen_andc_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ case 5: /* ORN */
+ tcg_gen_orc_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ case 6: /* EON */
+ tcg_gen_eqv_i64(tcg_rd, tcg_rn, tcg_rm);
+ break;
+ default:
+ assert(FALSE);
+ break;
+ }
+
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+
+ if (opc == 3) {
+ gen_logic_CC(sf, tcg_rd);
+ }
+}
+
+/*
+ * C3.5.1 Add/subtract (extended register)
+ *
+ * 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0|
+ * +--+--+--+-----------+-----+--+-------+------+------+----+----+
+ * |sf|op| S| 0 1 0 1 1 | opt | 1| Rm |option| imm3 | Rn | Rd |
+ * +--+--+--+-----------+-----+--+-------+------+------+----+----+
+ *
+ * sf: 0 -> 32bit, 1 -> 64bit
+ * op: 0 -> add , 1 -> sub
+ * S: 1 -> set flags
+ * opt: 00
+ * option: extension type (see DecodeRegExtend)
+ * imm3: optional shift to Rm
+ *
+ * Rd = Rn + LSL(extend(Rm), amount)
+ */
+static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int imm3 = extract32(insn, 10, 3);
+ int option = extract32(insn, 13, 3);
+ int rm = extract32(insn, 16, 5);
+ bool setflags = extract32(insn, 29, 1);
+ bool sub_op = extract32(insn, 30, 1);
+ bool sf = extract32(insn, 31, 1);
+
+ TCGv_i64 tcg_rm, tcg_rn; /* temps */
+ TCGv_i64 tcg_rd;
+ TCGv_i64 tcg_result;
+
+ if (imm3 > 4) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* non-flag setting ops may use SP */
+ if (!setflags) {
+ tcg_rn = read_cpu_reg_sp(s, rn, sf);
+ tcg_rd = cpu_reg_sp(s, rd);
+ } else {
+ tcg_rn = read_cpu_reg(s, rn, sf);
+ tcg_rd = cpu_reg(s, rd);
+ }
+
+ tcg_rm = read_cpu_reg(s, rm, sf);
+ ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3);
+
+ tcg_result = tcg_temp_new_i64();
+
+ if (!setflags) {
+ if (sub_op) {
+ tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm);
+ } else {
+ tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm);
+ }
+ } else {
+ if (sub_op) {
+ gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm);
+ } else {
+ gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm);
+ }
+ }
+
+ if (sf) {
+ tcg_gen_mov_i64(tcg_rd, tcg_result);
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_result);
+ }
+
+ tcg_temp_free_i64(tcg_result);
+}
+
+/*
+ * C3.5.2 Add/subtract (shifted register)
+ *
+ * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0
+ * +--+--+--+-----------+-----+--+-------+---------+------+------+
+ * |sf|op| S| 0 1 0 1 1 |shift| 0| Rm | imm6 | Rn | Rd |
+ * +--+--+--+-----------+-----+--+-------+---------+------+------+
+ *
+ * sf: 0 -> 32bit, 1 -> 64bit
+ * op: 0 -> add , 1 -> sub
+ * S: 1 -> set flags
+ * shift: 00 -> LSL, 01 -> LSR, 10 -> ASR, 11 -> RESERVED
+ * imm6: Shift amount to apply to Rm before the add/sub
+ */
+static void disas_add_sub_reg(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int imm6 = extract32(insn, 10, 6);
+ int rm = extract32(insn, 16, 5);
+ int shift_type = extract32(insn, 22, 2);
+ bool setflags = extract32(insn, 29, 1);
+ bool sub_op = extract32(insn, 30, 1);
+ bool sf = extract32(insn, 31, 1);
+
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+ TCGv_i64 tcg_rn, tcg_rm;
+ TCGv_i64 tcg_result;
+
+ if ((shift_type == 3) || (!sf && (imm6 > 31))) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ tcg_rn = read_cpu_reg(s, rn, sf);
+ tcg_rm = read_cpu_reg(s, rm, sf);
+
+ shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6);
+
+ tcg_result = tcg_temp_new_i64();
+
+ if (!setflags) {
+ if (sub_op) {
+ tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm);
+ } else {
+ tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm);
+ }
+ } else {
+ if (sub_op) {
+ gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm);
+ } else {
+ gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm);
+ }
+ }
+
+ if (sf) {
+ tcg_gen_mov_i64(tcg_rd, tcg_result);
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_result);
+ }
+
+ tcg_temp_free_i64(tcg_result);
+}
+
+/* C3.5.9 Data-processing (3 source)
+
+ 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0
+ +--+------+-----------+------+------+----+------+------+------+
+ |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd |
+ +--+------+-----------+------+------+----+------+------+------+
+
+ */
+static void disas_data_proc_3src(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int ra = extract32(insn, 10, 5);
+ int rm = extract32(insn, 16, 5);
+ int op_id = (extract32(insn, 29, 3) << 4) |
+ (extract32(insn, 21, 3) << 1) |
+ extract32(insn, 15, 1);
+ bool sf = extract32(insn, 31, 1);
+ bool is_sub = extract32(op_id, 0, 1);
+ bool is_high = extract32(op_id, 2, 1);
+ bool is_signed = false;
+ TCGv_i64 tcg_op1;
+ TCGv_i64 tcg_op2;
+ TCGv_i64 tcg_tmp;
+
+ /* Note that op_id is sf:op54:op31:o0 so it includes the 32/64 size flag */
+ switch (op_id) {
+ case 0x42: /* SMADDL */
+ case 0x43: /* SMSUBL */
+ case 0x44: /* SMULH */
+ is_signed = true;
+ break;
+ case 0x0: /* MADD (32bit) */
+ case 0x1: /* MSUB (32bit) */
+ case 0x40: /* MADD (64bit) */
+ case 0x41: /* MSUB (64bit) */
+ case 0x4a: /* UMADDL */
+ case 0x4b: /* UMSUBL */
+ case 0x4c: /* UMULH */
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (is_high) {
+ TCGv_i64 low_bits = tcg_temp_new_i64(); /* low bits discarded */
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+ TCGv_i64 tcg_rn = cpu_reg(s, rn);
+ TCGv_i64 tcg_rm = cpu_reg(s, rm);
+
+ if (is_signed) {
+ tcg_gen_muls2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm);
+ } else {
+ tcg_gen_mulu2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm);
+ }
+
+ tcg_temp_free_i64(low_bits);
+ return;
+ }
+
+ tcg_op1 = tcg_temp_new_i64();
+ tcg_op2 = tcg_temp_new_i64();
+ tcg_tmp = tcg_temp_new_i64();
+
+ if (op_id < 0x42) {
+ tcg_gen_mov_i64(tcg_op1, cpu_reg(s, rn));
+ tcg_gen_mov_i64(tcg_op2, cpu_reg(s, rm));
+ } else {
+ if (is_signed) {
+ tcg_gen_ext32s_i64(tcg_op1, cpu_reg(s, rn));
+ tcg_gen_ext32s_i64(tcg_op2, cpu_reg(s, rm));
+ } else {
+ tcg_gen_ext32u_i64(tcg_op1, cpu_reg(s, rn));
+ tcg_gen_ext32u_i64(tcg_op2, cpu_reg(s, rm));
+ }
+ }
+
+ if (ra == 31 && !is_sub) {
+ /* Special-case MADD with rA == XZR; it is the standard MUL alias */
+ tcg_gen_mul_i64(cpu_reg(s, rd), tcg_op1, tcg_op2);
+ } else {
+ tcg_gen_mul_i64(tcg_tmp, tcg_op1, tcg_op2);
+ if (is_sub) {
+ tcg_gen_sub_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp);
+ } else {
+ tcg_gen_add_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp);
+ }
+ }
+
+ if (!sf) {
+ tcg_gen_ext32u_i64(cpu_reg(s, rd), cpu_reg(s, rd));
+ }
+
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ tcg_temp_free_i64(tcg_tmp);
+}
+
+/* C3.5.3 - Add/subtract (with carry)
+ * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0
+ * +--+--+--+------------------------+------+---------+------+-----+
+ * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | opcode2 | Rn | Rd |
+ * +--+--+--+------------------------+------+---------+------+-----+
+ * [000000]
+ */
+
+static void disas_adc_sbc(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, op, setflags, rm, rn, rd;
+ TCGv_i64 tcg_y, tcg_rn, tcg_rd;
+
+ if (extract32(insn, 10, 6) != 0) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ sf = extract32(insn, 31, 1);
+ op = extract32(insn, 30, 1);
+ setflags = extract32(insn, 29, 1);
+ rm = extract32(insn, 16, 5);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ tcg_rd = cpu_reg(s, rd);
+ tcg_rn = cpu_reg(s, rn);
+
+ if (op) {
+ tcg_y = new_tmp_a64(s);
+ tcg_gen_not_i64(tcg_y, cpu_reg(s, rm));
+ } else {
+ tcg_y = cpu_reg(s, rm);
+ }
+
+ if (setflags) {
+ gen_adc_CC(sf, tcg_rd, tcg_rn, tcg_y);
+ } else {
+ gen_adc(sf, tcg_rd, tcg_rn, tcg_y);
+ }
+}
+
+/* C3.5.4 - C3.5.5 Conditional compare (immediate / register)
+ * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0
+ * +--+--+--+------------------------+--------+------+----+--+------+--+-----+
+ * |sf|op| S| 1 1 0 1 0 0 1 0 |imm5/rm | cond |i/r |o2| Rn |o3|nzcv |
+ * +--+--+--+------------------------+--------+------+----+--+------+--+-----+
+ * [1] y [0] [0]
+ */
+static void disas_cc(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, op, y, cond, rn, nzcv, is_imm;
+ int label_continue = -1;
+ TCGv_i64 tcg_tmp, tcg_y, tcg_rn;
+
+ if (!extract32(insn, 29, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+ if (insn & (1 << 10 | 1 << 4)) {
+ unallocated_encoding(s);
+ return;
+ }
+ sf = extract32(insn, 31, 1);
+ op = extract32(insn, 30, 1);
+ is_imm = extract32(insn, 11, 1);
+ y = extract32(insn, 16, 5); /* y = rm (reg) or imm5 (imm) */
+ cond = extract32(insn, 12, 4);
+ rn = extract32(insn, 5, 5);
+ nzcv = extract32(insn, 0, 4);
+
+ if (cond < 0x0e) { /* not always */
+ int label_match = gen_new_label();
+ label_continue = gen_new_label();
+ arm_gen_test_cc(cond, label_match);
+ /* nomatch: */
+ tcg_tmp = tcg_temp_new_i64();
+ tcg_gen_movi_i64(tcg_tmp, nzcv << 28);
+ gen_set_nzcv(tcg_tmp);
+ tcg_temp_free_i64(tcg_tmp);
+ tcg_gen_br(label_continue);
+ gen_set_label(label_match);
+ }
+ /* match, or condition is always */
+ if (is_imm) {
+ tcg_y = new_tmp_a64(s);
+ tcg_gen_movi_i64(tcg_y, y);
+ } else {
+ tcg_y = cpu_reg(s, y);
+ }
+ tcg_rn = cpu_reg(s, rn);
+
+ tcg_tmp = tcg_temp_new_i64();
+ if (op) {
+ gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y);
+ } else {
+ gen_add_CC(sf, tcg_tmp, tcg_rn, tcg_y);
+ }
+ tcg_temp_free_i64(tcg_tmp);
+
+ if (cond < 0x0e) { /* continue */
+ gen_set_label(label_continue);
+ }
+}
+
+/* C3.5.6 Conditional select
+ * 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0
+ * +----+----+---+-----------------+------+------+-----+------+------+
+ * | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd |
+ * +----+----+---+-----------------+------+------+-----+------+------+
+ */
+static void disas_cond_select(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, else_inv, rm, cond, else_inc, rn, rd;
+ TCGv_i64 tcg_rd, tcg_src;
+
+ if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) {
+ /* S == 1 or op2<1> == 1 */
+ unallocated_encoding(s);
+ return;
+ }
+ sf = extract32(insn, 31, 1);
+ else_inv = extract32(insn, 30, 1);
+ rm = extract32(insn, 16, 5);
+ cond = extract32(insn, 12, 4);
+ else_inc = extract32(insn, 10, 1);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ if (rd == 31) {
+ /* silly no-op write; until we use movcond we must special-case
+ * this to avoid a dead temporary across basic blocks.
+ */
+ return;
+ }
+
+ tcg_rd = cpu_reg(s, rd);
+
+ if (cond >= 0x0e) { /* condition "always" */
+ tcg_src = read_cpu_reg(s, rn, sf);
+ tcg_gen_mov_i64(tcg_rd, tcg_src);
+ } else {
+ /* OPTME: we could use movcond here, at the cost of duplicating
+ * a lot of the arm_gen_test_cc() logic.
+ */
+ int label_match = gen_new_label();
+ int label_continue = gen_new_label();
+
+ arm_gen_test_cc(cond, label_match);
+ /* nomatch: */
+ tcg_src = cpu_reg(s, rm);
+
+ if (else_inv && else_inc) {
+ tcg_gen_neg_i64(tcg_rd, tcg_src);
+ } else if (else_inv) {
+ tcg_gen_not_i64(tcg_rd, tcg_src);
+ } else if (else_inc) {
+ tcg_gen_addi_i64(tcg_rd, tcg_src, 1);
+ } else {
+ tcg_gen_mov_i64(tcg_rd, tcg_src);
+ }
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+ tcg_gen_br(label_continue);
+ /* match: */
+ gen_set_label(label_match);
+ tcg_src = read_cpu_reg(s, rn, sf);
+ tcg_gen_mov_i64(tcg_rd, tcg_src);
+ /* continue: */
+ gen_set_label(label_continue);
+ }
+}
+
+static void handle_clz(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_rd, tcg_rn;
+ tcg_rd = cpu_reg(s, rd);
+ tcg_rn = cpu_reg(s, rn);
+
+ if (sf) {
+ gen_helper_clz64(tcg_rd, tcg_rn);
+ } else {
+ TCGv_i32 tcg_tmp32 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn);
+ gen_helper_clz(tcg_tmp32, tcg_tmp32);
+ tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32);
+ tcg_temp_free_i32(tcg_tmp32);
+ }
+}
+
+static void handle_cls(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_rd, tcg_rn;
+ tcg_rd = cpu_reg(s, rd);
+ tcg_rn = cpu_reg(s, rn);
+
+ if (sf) {
+ gen_helper_cls64(tcg_rd, tcg_rn);
+ } else {
+ TCGv_i32 tcg_tmp32 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn);
+ gen_helper_cls32(tcg_tmp32, tcg_tmp32);
+ tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32);
+ tcg_temp_free_i32(tcg_tmp32);
+ }
+}
+
+static void handle_rbit(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_rd, tcg_rn;
+ tcg_rd = cpu_reg(s, rd);
+ tcg_rn = cpu_reg(s, rn);
+
+ if (sf) {
+ gen_helper_rbit64(tcg_rd, tcg_rn);
+ } else {
+ TCGv_i32 tcg_tmp32 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tcg_tmp32, tcg_rn);
+ gen_helper_rbit(tcg_tmp32, tcg_tmp32);
+ tcg_gen_extu_i32_i64(tcg_rd, tcg_tmp32);
+ tcg_temp_free_i32(tcg_tmp32);
+ }
+}
+
+/* C5.6.149 REV with sf==1, opcode==3 ("REV64") */
+static void handle_rev64(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ if (!sf) {
+ unallocated_encoding(s);
+ return;
+ }
+ tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn));
+}
+
+/* C5.6.149 REV with sf==0, opcode==2
+ * C5.6.151 REV32 (sf==1, opcode==2)
+ */
+static void handle_rev32(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+
+ if (sf) {
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+ TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf);
+
+ /* bswap32_i64 requires zero high word */
+ tcg_gen_ext32u_i64(tcg_tmp, tcg_rn);
+ tcg_gen_bswap32_i64(tcg_rd, tcg_tmp);
+ tcg_gen_shri_i64(tcg_tmp, tcg_rn, 32);
+ tcg_gen_bswap32_i64(tcg_tmp, tcg_tmp);
+ tcg_gen_concat32_i64(tcg_rd, tcg_rd, tcg_tmp);
+
+ tcg_temp_free_i64(tcg_tmp);
+ } else {
+ tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rn));
+ tcg_gen_bswap32_i64(tcg_rd, tcg_rd);
+ }
+}
+
+/* C5.6.150 REV16 (opcode==1) */
+static void handle_rev16(DisasContext *s, unsigned int sf,
+ unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+ TCGv_i64 tcg_tmp = tcg_temp_new_i64();
+ TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf);
+
+ tcg_gen_andi_i64(tcg_tmp, tcg_rn, 0xffff);
+ tcg_gen_bswap16_i64(tcg_rd, tcg_tmp);
+
+ tcg_gen_shri_i64(tcg_tmp, tcg_rn, 16);
+ tcg_gen_andi_i64(tcg_tmp, tcg_tmp, 0xffff);
+ tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp);
+ tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 16, 16);
+
+ if (sf) {
+ tcg_gen_shri_i64(tcg_tmp, tcg_rn, 32);
+ tcg_gen_andi_i64(tcg_tmp, tcg_tmp, 0xffff);
+ tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp);
+ tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 32, 16);
+
+ tcg_gen_shri_i64(tcg_tmp, tcg_rn, 48);
+ tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp);
+ tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_tmp, 48, 16);
+ }
+
+ tcg_temp_free_i64(tcg_tmp);
+}
+
+/* C3.5.7 Data-processing (1 source)
+ * 31 30 29 28 21 20 16 15 10 9 5 4 0
+ * +----+---+---+-----------------+---------+--------+------+------+
+ * | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd |
+ * +----+---+---+-----------------+---------+--------+------+------+
+ */
+static void disas_data_proc_1src(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, opcode, rn, rd;
+
+ if (extract32(insn, 29, 1) || extract32(insn, 16, 5)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ sf = extract32(insn, 31, 1);
+ opcode = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ switch (opcode) {
+ case 0: /* RBIT */
+ handle_rbit(s, sf, rn, rd);
+ break;
+ case 1: /* REV16 */
+ handle_rev16(s, sf, rn, rd);
+ break;
+ case 2: /* REV32 */
+ handle_rev32(s, sf, rn, rd);
+ break;
+ case 3: /* REV64 */
+ handle_rev64(s, sf, rn, rd);
+ break;
+ case 4: /* CLZ */
+ handle_clz(s, sf, rn, rd);
+ break;
+ case 5: /* CLS */
+ handle_cls(s, sf, rn, rd);
+ break;
+ }
+}
+
+static void handle_div(DisasContext *s, bool is_signed, unsigned int sf,
+ unsigned int rm, unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_n, tcg_m, tcg_rd;
+ tcg_rd = cpu_reg(s, rd);
+
+ if (!sf && is_signed) {
+ tcg_n = new_tmp_a64(s);
+ tcg_m = new_tmp_a64(s);
+ tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, rn));
+ tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, rm));
+ } else {
+ tcg_n = read_cpu_reg(s, rn, sf);
+ tcg_m = read_cpu_reg(s, rm, sf);
+ }
+
+ if (is_signed) {
+ gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m);
+ } else {
+ gen_helper_udiv64(tcg_rd, tcg_n, tcg_m);
+ }
+
+ if (!sf) { /* zero extend final result */
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+}
+
+/* C5.6.115 LSLV, C5.6.118 LSRV, C5.6.17 ASRV, C5.6.154 RORV */
+static void handle_shift_reg(DisasContext *s,
+ enum a64_shift_type shift_type, unsigned int sf,
+ unsigned int rm, unsigned int rn, unsigned int rd)
+{
+ TCGv_i64 tcg_shift = tcg_temp_new_i64();
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+ TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf);
+
+ tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31);
+ shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift);
+ tcg_temp_free_i64(tcg_shift);
+}
+
+/* C3.5.8 Data-processing (2 source)
+ * 31 30 29 28 21 20 16 15 10 9 5 4 0
+ * +----+---+---+-----------------+------+--------+------+------+
+ * | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd |
+ * +----+---+---+-----------------+------+--------+------+------+
+ */
+static void disas_data_proc_2src(DisasContext *s, uint32_t insn)
+{
+ unsigned int sf, rm, opcode, rn, rd;
+ sf = extract32(insn, 31, 1);
+ rm = extract32(insn, 16, 5);
+ opcode = extract32(insn, 10, 6);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ if (extract32(insn, 29, 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (opcode) {
+ case 2: /* UDIV */
+ handle_div(s, false, sf, rm, rn, rd);
+ break;
+ case 3: /* SDIV */
+ handle_div(s, true, sf, rm, rn, rd);
+ break;
+ case 8: /* LSLV */
+ handle_shift_reg(s, A64_SHIFT_TYPE_LSL, sf, rm, rn, rd);
+ break;
+ case 9: /* LSRV */
+ handle_shift_reg(s, A64_SHIFT_TYPE_LSR, sf, rm, rn, rd);
+ break;
+ case 10: /* ASRV */
+ handle_shift_reg(s, A64_SHIFT_TYPE_ASR, sf, rm, rn, rd);
+ break;
+ case 11: /* RORV */
+ handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd);
+ break;
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23: /* CRC32 */
+ unsupported_encoding(s, insn);
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* C3.5 Data processing - register */
+static void disas_data_proc_reg(DisasContext *s, uint32_t insn)
+{
+ switch (extract32(insn, 24, 5)) {
+ case 0x0a: /* Logical (shifted register) */
+ disas_logic_reg(s, insn);
+ break;
+ case 0x0b: /* Add/subtract */
+ if (insn & (1 << 21)) { /* (extended register) */
+ disas_add_sub_ext_reg(s, insn);
+ } else {
+ disas_add_sub_reg(s, insn);
+ }
+ break;
+ case 0x1b: /* Data-processing (3 source) */
+ disas_data_proc_3src(s, insn);
+ break;
+ case 0x1a:
+ switch (extract32(insn, 21, 3)) {
+ case 0x0: /* Add/subtract (with carry) */
+ disas_adc_sbc(s, insn);
+ break;
+ case 0x2: /* Conditional compare */
+ disas_cc(s, insn); /* both imm and reg forms */
+ break;
+ case 0x4: /* Conditional select */
+ disas_cond_select(s, insn);
+ break;
+ case 0x6: /* Data-processing */
+ if (insn & (1 << 30)) { /* (1 source) */
+ disas_data_proc_1src(s, insn);
+ } else { /* (2 source) */
+ disas_data_proc_2src(s, insn);
+ }
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* Convert ARM rounding mode to softfloat */
+static inline int arm_rmode_to_sf(int rmode)
+{
+ switch (rmode) {
+ case FPROUNDING_TIEAWAY:
+ rmode = float_round_ties_away;
+ break;
+ case FPROUNDING_ODD:
+ /* FIXME: add support for TIEAWAY and ODD */
+ qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n",
+ rmode);
+ case FPROUNDING_TIEEVEN:
+ default:
+ rmode = float_round_nearest_even;
+ break;
+ case FPROUNDING_POSINF:
+ rmode = float_round_up;
+ break;
+ case FPROUNDING_NEGINF:
+ rmode = float_round_down;
+ break;
+ case FPROUNDING_ZERO:
+ rmode = float_round_to_zero;
+ break;
+ }
+ return rmode;
+}
+
+static void handle_fp_compare(DisasContext *s, bool is_double,
+ unsigned int rn, unsigned int rm,
+ bool cmp_with_zero, bool signal_all_nans)
+{
+ TCGv_i64 tcg_flags = tcg_temp_new_i64();
+ TCGv_ptr fpst = get_fpstatus_ptr();
+
+ if (is_double) {
+ TCGv_i64 tcg_vn, tcg_vm;
+
+ tcg_vn = read_fp_dreg(s, rn);
+ if (cmp_with_zero) {
+ tcg_vm = tcg_const_i64(0);
+ } else {
+ tcg_vm = read_fp_dreg(s, rm);
+ }
+ if (signal_all_nans) {
+ gen_helper_vfp_cmped_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
+ } else {
+ gen_helper_vfp_cmpd_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
+ }
+ tcg_temp_free_i64(tcg_vn);
+ tcg_temp_free_i64(tcg_vm);
+ } else {
+ TCGv_i32 tcg_vn, tcg_vm;
+
+ tcg_vn = read_fp_sreg(s, rn);
+ if (cmp_with_zero) {
+ tcg_vm = tcg_const_i32(0);
+ } else {
+ tcg_vm = read_fp_sreg(s, rm);
+ }
+ if (signal_all_nans) {
+ gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
+ } else {
+ gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst);
+ }
+ tcg_temp_free_i32(tcg_vn);
+ tcg_temp_free_i32(tcg_vm);
+ }
+
+ tcg_temp_free_ptr(fpst);
+
+ gen_set_nzcv(tcg_flags);
+
+ tcg_temp_free_i64(tcg_flags);
+}
+
+/* C3.6.22 Floating point compare
+ * 31 30 29 28 24 23 22 21 20 16 15 14 13 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------+-----+---------+------+-------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | op | 1 0 0 0 | Rn | op2 |
+ * +---+---+---+-----------+------+---+------+-----+---------+------+-------+
+ */
+static void disas_fp_compare(DisasContext *s, uint32_t insn)
+{
+ unsigned int mos, type, rm, op, rn, opc, op2r;
+
+ mos = extract32(insn, 29, 3);
+ type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
+ rm = extract32(insn, 16, 5);
+ op = extract32(insn, 14, 2);
+ rn = extract32(insn, 5, 5);
+ opc = extract32(insn, 3, 2);
+ op2r = extract32(insn, 0, 3);
+
+ if (mos || op || op2r || type > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2);
+}
+
+/* C3.6.23 Floating point conditional compare
+ * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0
+ * +---+---+---+-----------+------+---+------+------+-----+------+----+------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 0 1 | Rn | op | nzcv |
+ * +---+---+---+-----------+------+---+------+------+-----+------+----+------+
+ */
+static void disas_fp_ccomp(DisasContext *s, uint32_t insn)
+{
+ unsigned int mos, type, rm, cond, rn, op, nzcv;
+ TCGv_i64 tcg_flags;
+ int label_continue = -1;
+
+ mos = extract32(insn, 29, 3);
+ type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
+ rm = extract32(insn, 16, 5);
+ cond = extract32(insn, 12, 4);
+ rn = extract32(insn, 5, 5);
+ op = extract32(insn, 4, 1);
+ nzcv = extract32(insn, 0, 4);
+
+ if (mos || type > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (cond < 0x0e) { /* not always */
+ int label_match = gen_new_label();
+ label_continue = gen_new_label();
+ arm_gen_test_cc(cond, label_match);
+ /* nomatch: */
+ tcg_flags = tcg_const_i64(nzcv << 28);
+ gen_set_nzcv(tcg_flags);
+ tcg_temp_free_i64(tcg_flags);
+ tcg_gen_br(label_continue);
+ gen_set_label(label_match);
+ }
+
+ handle_fp_compare(s, type, rn, rm, false, op);
+
+ if (cond < 0x0e) {
+ gen_set_label(label_continue);
+ }
+}
+
+/* copy src FP register to dst FP register; type specifies single or double */
+static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src)
+{
+ if (type) {
+ TCGv_i64 v = read_fp_dreg(s, src);
+ write_fp_dreg(s, dst, v);
+ tcg_temp_free_i64(v);
+ } else {
+ TCGv_i32 v = read_fp_sreg(s, src);
+ write_fp_sreg(s, dst, v);
+ tcg_temp_free_i32(v);
+ }
+}
+
+/* C3.6.24 Floating point conditional select
+ * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------+------+-----+------+------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 1 1 | Rn | Rd |
+ * +---+---+---+-----------+------+---+------+------+-----+------+------+
+ */
+static void disas_fp_csel(DisasContext *s, uint32_t insn)
+{
+ unsigned int mos, type, rm, cond, rn, rd;
+ int label_continue = -1;
+
+ mos = extract32(insn, 29, 3);
+ type = extract32(insn, 22, 2); /* 0 = single, 1 = double */
+ rm = extract32(insn, 16, 5);
+ cond = extract32(insn, 12, 4);
+ rn = extract32(insn, 5, 5);
+ rd = extract32(insn, 0, 5);
+
+ if (mos || type > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (cond < 0x0e) { /* not always */
+ int label_match = gen_new_label();
+ label_continue = gen_new_label();
+ arm_gen_test_cc(cond, label_match);
+ /* nomatch: */
+ gen_mov_fp2fp(s, type, rd, rm);
+ tcg_gen_br(label_continue);
+ gen_set_label(label_match);
+ }
+
+ gen_mov_fp2fp(s, type, rd, rn);
+
+ if (cond < 0x0e) { /* continue */
+ gen_set_label(label_continue);
+ }
+}
+
+/* C3.6.25 Floating-point data-processing (1 source) - single precision */
+static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
+{
+ TCGv_ptr fpst;
+ TCGv_i32 tcg_op;
+ TCGv_i32 tcg_res;
+
+ fpst = get_fpstatus_ptr();
+ tcg_op = read_fp_sreg(s, rn);
+ tcg_res = tcg_temp_new_i32();
+
+ switch (opcode) {
+ case 0x0: /* FMOV */
+ tcg_gen_mov_i32(tcg_res, tcg_op);
+ break;
+ case 0x1: /* FABS */
+ gen_helper_vfp_abss(tcg_res, tcg_op);
+ break;
+ case 0x2: /* FNEG */
+ gen_helper_vfp_negs(tcg_res, tcg_op);
+ break;
+ case 0x3: /* FSQRT */
+ gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env);
+ break;
+ case 0x8: /* FRINTN */
+ case 0x9: /* FRINTP */
+ case 0xa: /* FRINTM */
+ case 0xb: /* FRINTZ */
+ case 0xc: /* FRINTA */
+ {
+ TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7));
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ gen_helper_rints(tcg_res, tcg_op, fpst);
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ break;
+ }
+ case 0xe: /* FRINTX */
+ gen_helper_rints_exact(tcg_res, tcg_op, fpst);
+ break;
+ case 0xf: /* FRINTI */
+ gen_helper_rints(tcg_res, tcg_op, fpst);
+ break;
+ default:
+ abort();
+ }
+
+ write_fp_sreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i32(tcg_op);
+ tcg_temp_free_i32(tcg_res);
+}
+
+/* C3.6.25 Floating-point data-processing (1 source) - double precision */
+static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
+{
+ TCGv_ptr fpst;
+ TCGv_i64 tcg_op;
+ TCGv_i64 tcg_res;
+
+ fpst = get_fpstatus_ptr();
+ tcg_op = read_fp_dreg(s, rn);
+ tcg_res = tcg_temp_new_i64();
+
+ switch (opcode) {
+ case 0x0: /* FMOV */
+ tcg_gen_mov_i64(tcg_res, tcg_op);
+ break;
+ case 0x1: /* FABS */
+ gen_helper_vfp_absd(tcg_res, tcg_op);
+ break;
+ case 0x2: /* FNEG */
+ gen_helper_vfp_negd(tcg_res, tcg_op);
+ break;
+ case 0x3: /* FSQRT */
+ gen_helper_vfp_sqrtd(tcg_res, tcg_op, cpu_env);
+ break;
+ case 0x8: /* FRINTN */
+ case 0x9: /* FRINTP */
+ case 0xa: /* FRINTM */
+ case 0xb: /* FRINTZ */
+ case 0xc: /* FRINTA */
+ {
+ TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7));
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ gen_helper_rintd(tcg_res, tcg_op, fpst);
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ break;
+ }
+ case 0xe: /* FRINTX */
+ gen_helper_rintd_exact(tcg_res, tcg_op, fpst);
+ break;
+ case 0xf: /* FRINTI */
+ gen_helper_rintd(tcg_res, tcg_op, fpst);
+ break;
+ default:
+ abort();
+ }
+
+ write_fp_dreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i64(tcg_op);
+ tcg_temp_free_i64(tcg_res);
+}
+
+static void handle_fp_fcvt(DisasContext *s, int opcode,
+ int rd, int rn, int dtype, int ntype)
+{
+ switch (ntype) {
+ case 0x0:
+ {
+ TCGv_i32 tcg_rn = read_fp_sreg(s, rn);
+ if (dtype == 1) {
+ /* Single to double */
+ TCGv_i64 tcg_rd = tcg_temp_new_i64();
+ gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, cpu_env);
+ write_fp_dreg(s, rd, tcg_rd);
+ tcg_temp_free_i64(tcg_rd);
+ } else {
+ /* Single to half */
+ TCGv_i32 tcg_rd = tcg_temp_new_i32();
+ gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, cpu_env);
+ /* write_fp_sreg is OK here because top half of tcg_rd is zero */
+ write_fp_sreg(s, rd, tcg_rd);
+ tcg_temp_free_i32(tcg_rd);
+ }
+ tcg_temp_free_i32(tcg_rn);
+ break;
+ }
+ case 0x1:
+ {
+ TCGv_i64 tcg_rn = read_fp_dreg(s, rn);
+ TCGv_i32 tcg_rd = tcg_temp_new_i32();
+ if (dtype == 0) {
+ /* Double to single */
+ gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_env);
+ } else {
+ /* Double to half */
+ gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, cpu_env);
+ /* write_fp_sreg is OK here because top half of tcg_rd is zero */
+ }
+ write_fp_sreg(s, rd, tcg_rd);
+ tcg_temp_free_i32(tcg_rd);
+ tcg_temp_free_i64(tcg_rn);
+ break;
+ }
+ case 0x3:
+ {
+ TCGv_i32 tcg_rn = read_fp_sreg(s, rn);
+ tcg_gen_ext16u_i32(tcg_rn, tcg_rn);
+ if (dtype == 0) {
+ /* Half to single */
+ TCGv_i32 tcg_rd = tcg_temp_new_i32();
+ gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, cpu_env);
+ write_fp_sreg(s, rd, tcg_rd);
+ tcg_temp_free_i32(tcg_rd);
+ } else {
+ /* Half to double */
+ TCGv_i64 tcg_rd = tcg_temp_new_i64();
+ gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, cpu_env);
+ write_fp_dreg(s, rd, tcg_rd);
+ tcg_temp_free_i64(tcg_rd);
+ }
+ tcg_temp_free_i32(tcg_rn);
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+/* C3.6.25 Floating point data-processing (1 source)
+ * 31 30 29 28 24 23 22 21 20 15 14 10 9 5 4 0
+ * +---+---+---+-----------+------+---+--------+-----------+------+------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | opcode | 1 0 0 0 0 | Rn | Rd |
+ * +---+---+---+-----------+------+---+--------+-----------+------+------+
+ */
+static void disas_fp_1src(DisasContext *s, uint32_t insn)
+{
+ int type = extract32(insn, 22, 2);
+ int opcode = extract32(insn, 15, 6);
+ int rn = extract32(insn, 5, 5);
+ int rd = extract32(insn, 0, 5);
+
+ switch (opcode) {
+ case 0x4: case 0x5: case 0x7:
+ {
+ /* FCVT between half, single and double precision */
+ int dtype = extract32(opcode, 0, 2);
+ if (type == 2 || dtype == type) {
+ unallocated_encoding(s);
+ return;
+ }
+ handle_fp_fcvt(s, opcode, rd, rn, dtype, type);
+ break;
+ }
+ case 0x0 ... 0x3:
+ case 0x8 ... 0xc:
+ case 0xe ... 0xf:
+ /* 32-to-32 and 64-to-64 ops */
+ switch (type) {
+ case 0:
+ handle_fp_1src_single(s, opcode, rd, rn);
+ break;
+ case 1:
+ handle_fp_1src_double(s, opcode, rd, rn);
+ break;
+ default:
+ unallocated_encoding(s);
+ }
+ break;
+ default:
+ unallocated_encoding(s);
+ break;
+ }
+}
+
+/* C3.6.26 Floating-point data-processing (2 source) - single precision */
+static void handle_fp_2src_single(DisasContext *s, int opcode,
+ int rd, int rn, int rm)
+{
+ TCGv_i32 tcg_op1;
+ TCGv_i32 tcg_op2;
+ TCGv_i32 tcg_res;
+ TCGv_ptr fpst;
+
+ tcg_res = tcg_temp_new_i32();
+ fpst = get_fpstatus_ptr();
+ tcg_op1 = read_fp_sreg(s, rn);
+ tcg_op2 = read_fp_sreg(s, rm);
+
+ switch (opcode) {
+ case 0x0: /* FMUL */
+ gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x1: /* FDIV */
+ gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x2: /* FADD */
+ gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3: /* FSUB */
+ gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x4: /* FMAX */
+ gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5: /* FMIN */
+ gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x6: /* FMAXNM */
+ gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x7: /* FMINNM */
+ gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x8: /* FNMUL */
+ gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst);
+ gen_helper_vfp_negs(tcg_res, tcg_res);
+ break;
+ }
+
+ write_fp_sreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i32(tcg_op1);
+ tcg_temp_free_i32(tcg_op2);
+ tcg_temp_free_i32(tcg_res);
+}
+
+/* C3.6.26 Floating-point data-processing (2 source) - double precision */
+static void handle_fp_2src_double(DisasContext *s, int opcode,
+ int rd, int rn, int rm)
+{
+ TCGv_i64 tcg_op1;
+ TCGv_i64 tcg_op2;
+ TCGv_i64 tcg_res;
+ TCGv_ptr fpst;
+
+ tcg_res = tcg_temp_new_i64();
+ fpst = get_fpstatus_ptr();
+ tcg_op1 = read_fp_dreg(s, rn);
+ tcg_op2 = read_fp_dreg(s, rm);
+
+ switch (opcode) {
+ case 0x0: /* FMUL */
+ gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x1: /* FDIV */
+ gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x2: /* FADD */
+ gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x3: /* FSUB */
+ gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x4: /* FMAX */
+ gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x5: /* FMIN */
+ gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x6: /* FMAXNM */
+ gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x7: /* FMINNM */
+ gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst);
+ break;
+ case 0x8: /* FNMUL */
+ gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst);
+ gen_helper_vfp_negd(tcg_res, tcg_res);
+ break;
+ }
+
+ write_fp_dreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ tcg_temp_free_i64(tcg_res);
+}
+
+/* C3.6.26 Floating point data-processing (2 source)
+ * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------+--------+-----+------+------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | opcode | 1 0 | Rn | Rd |
+ * +---+---+---+-----------+------+---+------+--------+-----+------+------+
+ */
+static void disas_fp_2src(DisasContext *s, uint32_t insn)
+{
+ int type = extract32(insn, 22, 2);
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int rm = extract32(insn, 16, 5);
+ int opcode = extract32(insn, 12, 4);
+
+ if (opcode > 8) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (type) {
+ case 0:
+ handle_fp_2src_single(s, opcode, rd, rn, rm);
+ break;
+ case 1:
+ handle_fp_2src_double(s, opcode, rd, rn, rm);
+ break;
+ default:
+ unallocated_encoding(s);
+ }
+}
+
+/* C3.6.27 Floating-point data-processing (3 source) - single precision */
+static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1,
+ int rd, int rn, int rm, int ra)
+{
+ TCGv_i32 tcg_op1, tcg_op2, tcg_op3;
+ TCGv_i32 tcg_res = tcg_temp_new_i32();
+ TCGv_ptr fpst = get_fpstatus_ptr();
+
+ tcg_op1 = read_fp_sreg(s, rn);
+ tcg_op2 = read_fp_sreg(s, rm);
+ tcg_op3 = read_fp_sreg(s, ra);
+
+ /* These are fused multiply-add, and must be done as one
+ * floating point operation with no rounding between the
+ * multiplication and addition steps.
+ * NB that doing the negations here as separate steps is
+ * correct : an input NaN should come out with its sign bit
+ * flipped if it is a negated-input.
+ */
+ if (o1 == true) {
+ gen_helper_vfp_negs(tcg_op3, tcg_op3);
+ }
+
+ if (o0 != o1) {
+ gen_helper_vfp_negs(tcg_op1, tcg_op1);
+ }
+
+ gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst);
+
+ write_fp_sreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i32(tcg_op1);
+ tcg_temp_free_i32(tcg_op2);
+ tcg_temp_free_i32(tcg_op3);
+ tcg_temp_free_i32(tcg_res);
+}
+
+/* C3.6.27 Floating-point data-processing (3 source) - double precision */
+static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
+ int rd, int rn, int rm, int ra)
+{
+ TCGv_i64 tcg_op1, tcg_op2, tcg_op3;
+ TCGv_i64 tcg_res = tcg_temp_new_i64();
+ TCGv_ptr fpst = get_fpstatus_ptr();
+
+ tcg_op1 = read_fp_dreg(s, rn);
+ tcg_op2 = read_fp_dreg(s, rm);
+ tcg_op3 = read_fp_dreg(s, ra);
+
+ /* These are fused multiply-add, and must be done as one
+ * floating point operation with no rounding between the
+ * multiplication and addition steps.
+ * NB that doing the negations here as separate steps is
+ * correct : an input NaN should come out with its sign bit
+ * flipped if it is a negated-input.
+ */
+ if (o1 == true) {
+ gen_helper_vfp_negd(tcg_op3, tcg_op3);
+ }
+
+ if (o0 != o1) {
+ gen_helper_vfp_negd(tcg_op1, tcg_op1);
+ }
+
+ gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst);
+
+ write_fp_dreg(s, rd, tcg_res);
+
+ tcg_temp_free_ptr(fpst);
+ tcg_temp_free_i64(tcg_op1);
+ tcg_temp_free_i64(tcg_op2);
+ tcg_temp_free_i64(tcg_op3);
+ tcg_temp_free_i64(tcg_res);
+}
+
+/* C3.6.27 Floating point data-processing (3 source)
+ * 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0
+ * +---+---+---+-----------+------+----+------+----+------+------+------+
+ * | M | 0 | S | 1 1 1 1 1 | type | o1 | Rm | o0 | Ra | Rn | Rd |
+ * +---+---+---+-----------+------+----+------+----+------+------+------+
+ */
+static void disas_fp_3src(DisasContext *s, uint32_t insn)
+{
+ int type = extract32(insn, 22, 2);
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int ra = extract32(insn, 10, 5);
+ int rm = extract32(insn, 16, 5);
+ bool o0 = extract32(insn, 15, 1);
+ bool o1 = extract32(insn, 21, 1);
+
+ switch (type) {
+ case 0:
+ handle_fp_3src_single(s, o0, o1, rd, rn, rm, ra);
+ break;
+ case 1:
+ handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra);
+ break;
+ default:
+ unallocated_encoding(s);
+ }
+}
+
+/* C3.6.28 Floating point immediate
+ * 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0
+ * +---+---+---+-----------+------+---+------------+-------+------+------+
+ * | M | 0 | S | 1 1 1 1 0 | type | 1 | imm8 | 1 0 0 | imm5 | Rd |
+ * +---+---+---+-----------+------+---+------------+-------+------+------+
+ */
+static void disas_fp_imm(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int imm8 = extract32(insn, 13, 8);
+ int is_double = extract32(insn, 22, 2);
+ uint64_t imm;
+ TCGv_i64 tcg_res;
+
+ if (is_double > 1) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ /* The imm8 encodes the sign bit, enough bits to represent
+ * an exponent in the range 01....1xx to 10....0xx,
+ * and the most significant 4 bits of the mantissa; see
+ * VFPExpandImm() in the v8 ARM ARM.
+ */
+ if (is_double) {
+ imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
+ (extract32(imm8, 6, 1) ? 0x3fc0 : 0x4000) |
+ extract32(imm8, 0, 6);
+ imm <<= 48;
+ } else {
+ imm = (extract32(imm8, 7, 1) ? 0x8000 : 0) |
+ (extract32(imm8, 6, 1) ? 0x3e00 : 0x4000) |
+ (extract32(imm8, 0, 6) << 3);
+ imm <<= 16;
+ }
+
+ tcg_res = tcg_const_i64(imm);
+ write_fp_dreg(s, rd, tcg_res);
+ tcg_temp_free_i64(tcg_res);
+}
+
+/* Handle floating point <=> fixed point conversions. Note that we can
+ * also deal with fp <=> integer conversions as a special case (scale == 64)
+ * OPTME: consider handling that special case specially or at least skipping
+ * the call to scalbn in the helpers for zero shifts.
+ */
+static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
+ bool itof, int rmode, int scale, int sf, int type)
+{
+ bool is_signed = !(opcode & 1);
+ bool is_double = type;
+ TCGv_ptr tcg_fpstatus;
+ TCGv_i32 tcg_shift;
+
+ tcg_fpstatus = get_fpstatus_ptr();
+
+ tcg_shift = tcg_const_i32(64 - scale);
+
+ if (itof) {
+ TCGv_i64 tcg_int = cpu_reg(s, rn);
+ if (!sf) {
+ TCGv_i64 tcg_extend = new_tmp_a64(s);
+
+ if (is_signed) {
+ tcg_gen_ext32s_i64(tcg_extend, tcg_int);
+ } else {
+ tcg_gen_ext32u_i64(tcg_extend, tcg_int);
+ }
+
+ tcg_int = tcg_extend;
+ }
+
+ if (is_double) {
+ TCGv_i64 tcg_double = tcg_temp_new_i64();
+ if (is_signed) {
+ gen_helper_vfp_sqtod(tcg_double, tcg_int,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_uqtod(tcg_double, tcg_int,
+ tcg_shift, tcg_fpstatus);
+ }
+ write_fp_dreg(s, rd, tcg_double);
+ tcg_temp_free_i64(tcg_double);
+ } else {
+ TCGv_i32 tcg_single = tcg_temp_new_i32();
+ if (is_signed) {
+ gen_helper_vfp_sqtos(tcg_single, tcg_int,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_uqtos(tcg_single, tcg_int,
+ tcg_shift, tcg_fpstatus);
+ }
+ write_fp_sreg(s, rd, tcg_single);
+ tcg_temp_free_i32(tcg_single);
+ }
+ } else {
+ TCGv_i64 tcg_int = cpu_reg(s, rd);
+ TCGv_i32 tcg_rmode;
+
+ if (extract32(opcode, 2, 1)) {
+ /* There are too many rounding modes to all fit into rmode,
+ * so FCVTA[US] is a special case.
+ */
+ rmode = FPROUNDING_TIEAWAY;
+ }
+
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+
+ if (is_double) {
+ TCGv_i64 tcg_double = read_fp_dreg(s, rn);
+ if (is_signed) {
+ if (!sf) {
+ gen_helper_vfp_tosld(tcg_int, tcg_double,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_tosqd(tcg_int, tcg_double,
+ tcg_shift, tcg_fpstatus);
+ }
+ } else {
+ if (!sf) {
+ gen_helper_vfp_tould(tcg_int, tcg_double,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_touqd(tcg_int, tcg_double,
+ tcg_shift, tcg_fpstatus);
+ }
+ }
+ tcg_temp_free_i64(tcg_double);
+ } else {
+ TCGv_i32 tcg_single = read_fp_sreg(s, rn);
+ if (sf) {
+ if (is_signed) {
+ gen_helper_vfp_tosqs(tcg_int, tcg_single,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_touqs(tcg_int, tcg_single,
+ tcg_shift, tcg_fpstatus);
+ }
+ } else {
+ TCGv_i32 tcg_dest = tcg_temp_new_i32();
+ if (is_signed) {
+ gen_helper_vfp_tosls(tcg_dest, tcg_single,
+ tcg_shift, tcg_fpstatus);
+ } else {
+ gen_helper_vfp_touls(tcg_dest, tcg_single,
+ tcg_shift, tcg_fpstatus);
+ }
+ tcg_gen_extu_i32_i64(tcg_int, tcg_dest);
+ tcg_temp_free_i32(tcg_dest);
+ }
+ tcg_temp_free_i32(tcg_single);
+ }
+
+ gen_helper_set_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+
+ if (!sf) {
+ tcg_gen_ext32u_i64(tcg_int, tcg_int);
+ }
+ }
+
+ tcg_temp_free_ptr(tcg_fpstatus);
+ tcg_temp_free_i32(tcg_shift);
+}
+
+/* C3.6.29 Floating point <-> fixed point conversions
+ * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0
+ * +----+---+---+-----------+------+---+-------+--------+-------+------+------+
+ * | sf | 0 | S | 1 1 1 1 0 | type | 0 | rmode | opcode | scale | Rn | Rd |
+ * +----+---+---+-----------+------+---+-------+--------+-------+------+------+
+ */
+static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int scale = extract32(insn, 10, 6);
+ int opcode = extract32(insn, 16, 3);
+ int rmode = extract32(insn, 19, 2);
+ int type = extract32(insn, 22, 2);
+ bool sbit = extract32(insn, 29, 1);
+ bool sf = extract32(insn, 31, 1);
+ bool itof;
+
+ if (sbit || (type > 1)
+ || (!sf && scale < 32)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch ((rmode << 3) | opcode) {
+ case 0x2: /* SCVTF */
+ case 0x3: /* UCVTF */
+ itof = true;
+ break;
+ case 0x18: /* FCVTZS */
+ case 0x19: /* FCVTZU */
+ itof = false;
+ break;
+ default:
+ unallocated_encoding(s);
+ return;
+ }
+
+ handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type);
+}
+
+static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
+{
+ /* FMOV: gpr to or from float, double, or top half of quad fp reg,
+ * without conversion.
+ */
+
+ if (itof) {
+ TCGv_i64 tcg_rn = cpu_reg(s, rn);
+
+ switch (type) {
+ case 0:
+ {
+ /* 32 bit */
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ext32u_i64(tmp, tcg_rn);
+ tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(rd, MO_64));
+ tcg_gen_movi_i64(tmp, 0);
+ tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(rd));
+ tcg_temp_free_i64(tmp);
+ break;
+ }
+ case 1:
+ {
+ /* 64 bit */
+ TCGv_i64 tmp = tcg_const_i64(0);
+ tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(rd, MO_64));
+ tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(rd));
+ tcg_temp_free_i64(tmp);
+ break;
+ }
+ case 2:
+ /* 64 bit to top half. */
+ tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(rd));
+ break;
+ }
+ } else {
+ TCGv_i64 tcg_rd = cpu_reg(s, rd);
+
+ switch (type) {
+ case 0:
+ /* 32 bit */
+ tcg_gen_ld32u_i64(tcg_rd, cpu_env, fp_reg_offset(rn, MO_32));
+ break;
+ case 1:
+ /* 64 bit */
+ tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_offset(rn, MO_64));
+ break;
+ case 2:
+ /* 64 bits from top half */
+ tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(rn));
+ break;
+ }
+ }
+}
+
+/* C3.6.30 Floating point <-> integer conversions
+ * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0
+ * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+
+ * | sf | 0 | S | 1 1 1 1 0 | type | 1 | rmode | opc | 0 0 0 0 0 0 | Rn | Rd |
+ * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+
+ */
+static void disas_fp_int_conv(DisasContext *s, uint32_t insn)
+{
+ int rd = extract32(insn, 0, 5);
+ int rn = extract32(insn, 5, 5);
+ int opcode = extract32(insn, 16, 3);
+ int rmode = extract32(insn, 19, 2);
+ int type = extract32(insn, 22, 2);
+ bool sbit = extract32(insn, 29, 1);
+ bool sf = extract32(insn, 31, 1);
+
+ if (sbit) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ if (opcode > 5) {
+ /* FMOV */
+ bool itof = opcode & 1;
+
+ if (rmode >= 2) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ switch (sf << 3 | type << 1 | rmode) {
+ case 0x0: /* 32 bit */
+ case 0xa: /* 64 bit */
+ case 0xd: /* 64 bit to top half of quad */
+ break;
+ default:
+ /* all other sf/type/rmode combinations are invalid */
+ unallocated_encoding(s);
+ break;
+ }
+
+ handle_fmov(s, rd, rn, type, itof);
+ } else {
+ /* actual FP conversions */
+ bool itof = extract32(opcode, 1, 1);
+
+ if (type > 1 || (rmode != 0 && opcode > 1)) {
+ unallocated_encoding(s);
+ return;
+ }
+
+ handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type);
+ }
+}
+
+/* FP-specific subcases of table C3-6 (SIMD and FP data processing)
+ * 31 30 29 28 25 24 0
+ * +---+---+---+---------+-----------------------------+
+ * | | 0 | | 1 1 1 1 | |
+ * +---+---+---+---------+-----------------------------+
+ */
+static void disas_data_proc_fp(DisasContext *s, uint32_t insn)
+{
+ if (extract32(insn, 24, 1)) {
+ /* Floating point data-processing (3 source) */
+ disas_fp_3src(s, insn);
+ } else if (extract32(insn, 21, 1) == 0) {
+ /* Floating point to fixed point conversions */
+ disas_fp_fixed_conv(s, insn);
+ } else {
+ switch (extract32(insn, 10, 2)) {
+ case 1:
+ /* Floating point conditional compare */
+ disas_fp_ccomp(s, insn);
+ break;
+ case 2:
+ /* Floating point data-processing (2 source) */
+ disas_fp_2src(s, insn);
+ break;
+ case 3:
+ /* Floating point conditional select */
+ disas_fp_csel(s, insn);
+ break;
+ case 0:
+ switch (ctz32(extract32(insn, 12, 4))) {
+ case 0: /* [15:12] == xxx1 */
+ /* Floating point immediate */
+ disas_fp_imm(s, insn);
+ break;
+ case 1: /* [15:12] == xx10 */
+ /* Floating point compare */
+ disas_fp_compare(s, insn);
+ break;
+ case 2: /* [15:12] == x100 */
+ /* Floating point data-processing (1 source) */
+ disas_fp_1src(s, insn);
+ break;
+ case 3: /* [15:12] == 1000 */
+ unallocated_encoding(s);
+ break;
+ default: /* [15:12] == 0000 */
+ /* Floating point <-> integer conversions */
+ disas_fp_int_conv(s, insn);
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void disas_data_proc_simd(DisasContext *s, uint32_t insn)
+{
+ /* Note that this is called with all non-FP cases from
+ * table C3-6 so it must UNDEF for entries not specifically
+ * allocated to instructions in that table.
+ */
+ unsupported_encoding(s, insn);
+}
+
+/* C3.6 Data processing - SIMD and floating point */
+static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn)
+{
+ if (extract32(insn, 28, 1) == 1 && extract32(insn, 30, 1) == 0) {
+ disas_data_proc_fp(s, insn);
+ } else {
+ /* SIMD, including crypto */
+ disas_data_proc_simd(s, insn);
+ }
+}
+
+/* C3.1 A64 instruction index by encoding */
+static void disas_a64_insn(CPUARMState *env, DisasContext *s)
{
uint32_t insn;
@@ -126,14 +4253,208 @@ void disas_a64_insn(CPUARMState *env, DisasContext *s)
s->insn = insn;
s->pc += 4;
- switch ((insn >> 24) & 0x1f) {
- default:
+ switch (extract32(insn, 25, 4)) {
+ case 0x0: case 0x1: case 0x2: case 0x3: /* UNALLOCATED */
unallocated_encoding(s);
break;
+ case 0x8: case 0x9: /* Data processing - immediate */
+ disas_data_proc_imm(s, insn);
+ break;
+ case 0xa: case 0xb: /* Branch, exception generation and system insns */
+ disas_b_exc_sys(s, insn);
+ break;
+ case 0x4:
+ case 0x6:
+ case 0xc:
+ case 0xe: /* Loads and stores */
+ disas_ldst(s, insn);
+ break;
+ case 0x5:
+ case 0xd: /* Data processing - register */
+ disas_data_proc_reg(s, insn);
+ break;
+ case 0x7:
+ case 0xf: /* Data processing - SIMD and floating point */
+ disas_data_proc_simd_fp(s, insn);
+ break;
+ default:
+ assert(FALSE); /* all 15 cases should be handled above */
+ break;
}
- if (unlikely(s->singlestep_enabled) && (s->is_jmp == DISAS_TB_JUMP)) {
- /* go through the main loop for single step */
- s->is_jmp = DISAS_JUMP;
+ /* if we allocated any temporaries, free them here */
+ free_tmp_a64(s);
+}
+
+void gen_intermediate_code_internal_a64(ARMCPU *cpu,
+ TranslationBlock *tb,
+ bool search_pc)
+{
+ CPUState *cs = CPU(cpu);
+ CPUARMState *env = &cpu->env;
+ DisasContext dc1, *dc = &dc1;
+ CPUBreakpoint *bp;
+ uint16_t *gen_opc_end;
+ int j, lj;
+ target_ulong pc_start;
+ target_ulong next_page_start;
+ int num_insns;
+ int max_insns;
+
+ pc_start = tb->pc;
+
+ dc->tb = tb;
+
+ gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = pc_start;
+ dc->singlestep_enabled = cs->singlestep_enabled;
+ dc->condjmp = 0;
+
+ dc->aarch64 = 1;
+ dc->thumb = 0;
+ dc->bswap_code = 0;
+ dc->condexec_mask = 0;
+ dc->condexec_cond = 0;
+#if !defined(CONFIG_USER_ONLY)
+ dc->user = 0;
+#endif
+ dc->vfp_enabled = 0;
+ dc->vec_len = 0;
+ dc->vec_stride = 0;
+ dc->cp_regs = cpu->cp_regs;
+ dc->current_pl = arm_current_pl(env);
+
+ init_tmp_a64_array(dc);
+
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0) {
+ max_insns = CF_COUNT_MASK;
+ }
+
+ gen_tb_start();
+
+ tcg_clear_temp_count();
+
+ do {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc->pc) {
+ gen_exception_insn(dc, 0, EXCP_DEBUG);
+ /* Advance PC so that clearing the breakpoint will
+ invalidate this TB. */
+ dc->pc += 2;
+ goto done_generating;
+ }
+ }
+ }
+
+ if (search_pc) {
+ j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j) {
+ tcg_ctx.gen_opc_instr_start[lj++] = 0;
+ }
+ }
+ tcg_ctx.gen_opc_pc[lj] = dc->pc;
+ tcg_ctx.gen_opc_instr_start[lj] = 1;
+ tcg_ctx.gen_opc_icount[lj] = num_insns;
+ }
+
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+ gen_io_start();
+ }
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
+ tcg_gen_debug_insn_start(dc->pc);
+ }
+
+ disas_a64_insn(env, dc);
+
+ if (tcg_check_temp_count()) {
+ fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n",
+ dc->pc);
+ }
+
+ /* Translation stops when a conditional branch is encountered.
+ * Otherwise the subsequent code could get translated several times.
+ * Also stop translation when a page boundary is reached. This
+ * ensures prefetch aborts occur at the right place.
+ */
+ num_insns++;
+ } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
+ !cs->singlestep_enabled &&
+ !singlestep &&
+ dc->pc < next_page_start &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ if (unlikely(cs->singlestep_enabled) && dc->is_jmp != DISAS_EXC) {
+ /* Note that this means single stepping WFI doesn't halt the CPU.
+ * For conditional branch insns this is harmless unreachable code as
+ * gen_goto_tb() has already handled emitting the debug exception
+ * (and thus a tb-jump is not possible when singlestepping).
+ */
+ assert(dc->is_jmp != DISAS_TB_JUMP);
+ if (dc->is_jmp != DISAS_JUMP) {
+ gen_a64_set_pc_im(dc->pc);
+ }
+ gen_exception(EXCP_DEBUG);
+ } else {
+ switch (dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_goto_tb(dc, 1, dc->pc);
+ break;
+ default:
+ case DISAS_UPDATE:
+ gen_a64_set_pc_im(dc->pc);
+ /* fall through */
+ case DISAS_JUMP:
+ /* indicate that the hash table must be used to find the next TB */
+ tcg_gen_exit_tb(0);
+ break;
+ case DISAS_TB_JUMP:
+ case DISAS_EXC:
+ case DISAS_SWI:
+ break;
+ case DISAS_WFI:
+ /* This is a special case because we don't want to just halt the CPU
+ * if trying to debug across a WFI.
+ */
+ gen_helper_wfi(cpu_env);
+ break;
+ }
+ }
+
+done_generating:
+ gen_tb_end(tb, num_insns);
+ *tcg_ctx.gen_opc_ptr = INDEX_op_end;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(env, pc_start, dc->pc - pc_start,
+ dc->thumb | (dc->bswap_code << 1));
+ qemu_log("\n");
+ }
+#endif
+ if (search_pc) {
+ j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
+ lj++;
+ while (lj <= j) {
+ tcg_ctx.gen_opc_instr_start[lj++] = 0;
+ }
+ } else {
+ tb->size = dc->pc - pc_start;
+ tb->icount = num_insns;
}
}
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 8c479ff9a8..8d240e160d 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -56,21 +56,15 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
#define IS_USER(s) (s->user)
#endif
-/* These instructions trap after executing, so defer them until after the
- conditional execution state has been updated. */
-#define DISAS_WFI 4
-#define DISAS_SWI 5
-
TCGv_ptr cpu_env;
/* We reuse the same 64-bit temporaries for efficiency. */
static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
static TCGv_i32 cpu_R[16];
static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
-static TCGv_i32 cpu_exclusive_addr;
-static TCGv_i32 cpu_exclusive_val;
-static TCGv_i32 cpu_exclusive_high;
+static TCGv_i64 cpu_exclusive_addr;
+static TCGv_i64 cpu_exclusive_val;
#ifdef CONFIG_USER_ONLY
-static TCGv_i32 cpu_exclusive_test;
+static TCGv_i64 cpu_exclusive_test;
static TCGv_i32 cpu_exclusive_info;
#endif
@@ -101,14 +95,12 @@ void arm_translate_init(void)
cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF");
cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF");
- cpu_exclusive_addr = tcg_global_mem_new_i32(TCG_AREG0,
+ cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_addr), "exclusive_addr");
- cpu_exclusive_val = tcg_global_mem_new_i32(TCG_AREG0,
+ cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_val), "exclusive_val");
- cpu_exclusive_high = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUARMState, exclusive_high), "exclusive_high");
#ifdef CONFIG_USER_ONLY
- cpu_exclusive_test = tcg_global_mem_new_i32(TCG_AREG0,
+ cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUARMState, exclusive_test), "exclusive_test");
cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUARMState, exclusive_info), "exclusive_info");
@@ -676,7 +668,11 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b)
}
#undef PAS_OP
-static void gen_test_cc(int cc, int label)
+/*
+ * generate a conditional branch based on ARM condition code cc.
+ * This is common between ARM and Aarch64 targets.
+ */
+void arm_gen_test_cc(int cc, int label)
{
TCGv_i32 tmp;
int inv;
@@ -900,11 +896,7 @@ DO_GEN_ST(32, MO_TEUL)
static inline void gen_set_pc_im(DisasContext *s, target_ulong val)
{
- if (s->aarch64) {
- gen_a64_set_pc_im(val);
- } else {
- tcg_gen_movi_i32(cpu_R[15], val);
- }
+ tcg_gen_movi_i32(cpu_R[15], val);
}
/* Force a TB lookup after an instruction that changes the CPU state. */
@@ -1106,27 +1098,29 @@ VFP_GEN_FTOI(tosi)
VFP_GEN_FTOI(tosiz)
#undef VFP_GEN_FTOI
-#define VFP_GEN_FIX(name) \
+#define VFP_GEN_FIX(name, round) \
static inline void gen_vfp_##name(int dp, int shift, int neon) \
{ \
TCGv_i32 tmp_shift = tcg_const_i32(shift); \
TCGv_ptr statusptr = get_fpstatus_ptr(neon); \
if (dp) { \
- gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \
+ gen_helper_vfp_##name##d##round(cpu_F0d, cpu_F0d, tmp_shift, \
+ statusptr); \
} else { \
- gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, statusptr); \
+ gen_helper_vfp_##name##s##round(cpu_F0s, cpu_F0s, tmp_shift, \
+ statusptr); \
} \
tcg_temp_free_i32(tmp_shift); \
tcg_temp_free_ptr(statusptr); \
}
-VFP_GEN_FIX(tosh)
-VFP_GEN_FIX(tosl)
-VFP_GEN_FIX(touh)
-VFP_GEN_FIX(toul)
-VFP_GEN_FIX(shto)
-VFP_GEN_FIX(slto)
-VFP_GEN_FIX(uhto)
-VFP_GEN_FIX(ulto)
+VFP_GEN_FIX(tosh, _round_to_zero)
+VFP_GEN_FIX(tosl, _round_to_zero)
+VFP_GEN_FIX(touh, _round_to_zero)
+VFP_GEN_FIX(toul, _round_to_zero)
+VFP_GEN_FIX(shto, )
+VFP_GEN_FIX(slto, )
+VFP_GEN_FIX(uhto, )
+VFP_GEN_FIX(ulto, )
#undef VFP_GEN_FIX
static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv_i32 addr)
@@ -2733,9 +2727,9 @@ static int handle_vminmaxnm(uint32_t insn, uint32_t rd, uint32_t rn,
tcg_gen_ld_f64(frn, cpu_env, vfp_reg_offset(dp, rn));
tcg_gen_ld_f64(frm, cpu_env, vfp_reg_offset(dp, rm));
if (vmin) {
- gen_helper_vfp_minnmd(dest, frn, frm, fpst);
+ gen_helper_vfp_minnumd(dest, frn, frm, fpst);
} else {
- gen_helper_vfp_maxnmd(dest, frn, frm, fpst);
+ gen_helper_vfp_maxnumd(dest, frn, frm, fpst);
}
tcg_gen_st_f64(dest, cpu_env, vfp_reg_offset(dp, rd));
tcg_temp_free_i64(frn);
@@ -2751,9 +2745,9 @@ static int handle_vminmaxnm(uint32_t insn, uint32_t rd, uint32_t rn,
tcg_gen_ld_f32(frn, cpu_env, vfp_reg_offset(dp, rn));
tcg_gen_ld_f32(frm, cpu_env, vfp_reg_offset(dp, rm));
if (vmin) {
- gen_helper_vfp_minnms(dest, frn, frm, fpst);
+ gen_helper_vfp_minnums(dest, frn, frm, fpst);
} else {
- gen_helper_vfp_maxnms(dest, frn, frm, fpst);
+ gen_helper_vfp_maxnums(dest, frn, frm, fpst);
}
tcg_gen_st_f32(dest, cpu_env, vfp_reg_offset(dp, rd));
tcg_temp_free_i32(frn);
@@ -4592,6 +4586,8 @@ static const uint8_t neon_3r_sizes[] = {
#define NEON_2RM_VREV16 2
#define NEON_2RM_VPADDL 4
#define NEON_2RM_VPADDL_U 5
+#define NEON_2RM_AESE 6 /* Includes AESD */
+#define NEON_2RM_AESMC 7 /* Includes AESIMC */
#define NEON_2RM_VCLS 8
#define NEON_2RM_VCLZ 9
#define NEON_2RM_VCNT 10
@@ -4649,6 +4645,8 @@ static const uint8_t neon_2rm_sizes[] = {
[NEON_2RM_VREV16] = 0x1,
[NEON_2RM_VPADDL] = 0x7,
[NEON_2RM_VPADDL_U] = 0x7,
+ [NEON_2RM_AESE] = 0x1,
+ [NEON_2RM_AESMC] = 0x1,
[NEON_2RM_VCLS] = 0x7,
[NEON_2RM_VCLZ] = 0x7,
[NEON_2RM_VCNT] = 0x1,
@@ -5125,9 +5123,9 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
{
TCGv_ptr fpstatus = get_fpstatus_ptr(1);
if (size == 0) {
- gen_helper_neon_max_f32(tmp, tmp, tmp2, fpstatus);
+ gen_helper_vfp_maxs(tmp, tmp, tmp2, fpstatus);
} else {
- gen_helper_neon_min_f32(tmp, tmp, tmp2, fpstatus);
+ gen_helper_vfp_mins(tmp, tmp, tmp2, fpstatus);
}
tcg_temp_free_ptr(fpstatus);
break;
@@ -5137,9 +5135,9 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
/* VMAXNM/VMINNM */
TCGv_ptr fpstatus = get_fpstatus_ptr(1);
if (size == 0) {
- gen_helper_vfp_maxnms(tmp, tmp, tmp2, fpstatus);
+ gen_helper_vfp_maxnums(tmp, tmp, tmp2, fpstatus);
} else {
- gen_helper_vfp_minnms(tmp, tmp, tmp2, fpstatus);
+ gen_helper_vfp_minnums(tmp, tmp, tmp2, fpstatus);
}
tcg_temp_free_ptr(fpstatus);
} else {
@@ -6184,6 +6182,28 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins
tcg_temp_free_i32(tmp2);
tcg_temp_free_i32(tmp3);
break;
+ case NEON_2RM_AESE: case NEON_2RM_AESMC:
+ if (!arm_feature(env, ARM_FEATURE_V8_AES)
+ || ((rm | rd) & 1)) {
+ return 1;
+ }
+ tmp = tcg_const_i32(rd);
+ tmp2 = tcg_const_i32(rm);
+
+ /* Bit 6 is the lowest opcode bit; it distinguishes between
+ * encryption (AESE/AESMC) and decryption (AESD/AESIMC)
+ */
+ tmp3 = tcg_const_i32(extract32(insn, 6, 1));
+
+ if (op == NEON_2RM_AESE) {
+ gen_helper_crypto_aese(cpu_env, tmp, tmp2, tmp3);
+ } else {
+ gen_helper_crypto_aesmc(cpu_env, tmp, tmp2, tmp3);
+ }
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ tcg_temp_free_i32(tmp3);
+ break;
default:
elementwise:
for (pass = 0; pass < (q ? 4 : 2); pass++) {
@@ -6477,7 +6497,6 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
{
int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2;
const ARMCPRegInfo *ri;
- ARMCPU *cpu = arm_env_get_cpu(env);
cpnum = (insn >> 8) & 0xf;
if (arm_feature(env, ARM_FEATURE_XSCALE)
@@ -6520,11 +6539,11 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
isread = (insn >> 20) & 1;
rt = (insn >> 12) & 0xf;
- ri = get_arm_cp_reginfo(cpu,
+ ri = get_arm_cp_reginfo(s->cp_regs,
ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2));
if (ri) {
/* Check access permissions */
- if (!cp_access_ok(env, ri, isread)) {
+ if (!cp_access_ok(s->current_pl, ri, isread)) {
return 1;
}
@@ -6738,30 +6757,34 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
default:
abort();
}
- tcg_gen_mov_i32(cpu_exclusive_val, tmp);
- store_reg(s, rt, tmp);
+
if (size == 3) {
TCGv_i32 tmp2 = tcg_temp_new_i32();
+ TCGv_i32 tmp3 = tcg_temp_new_i32();
+
tcg_gen_addi_i32(tmp2, addr, 4);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld32u(tmp, tmp2, IS_USER(s));
+ gen_aa32_ld32u(tmp3, tmp2, IS_USER(s));
tcg_temp_free_i32(tmp2);
- tcg_gen_mov_i32(cpu_exclusive_high, tmp);
- store_reg(s, rt2, tmp);
+ tcg_gen_concat_i32_i64(cpu_exclusive_val, tmp, tmp3);
+ store_reg(s, rt2, tmp3);
+ } else {
+ tcg_gen_extu_i32_i64(cpu_exclusive_val, tmp);
}
- tcg_gen_mov_i32(cpu_exclusive_addr, addr);
+
+ store_reg(s, rt, tmp);
+ tcg_gen_extu_i32_i64(cpu_exclusive_addr, addr);
}
static void gen_clrex(DisasContext *s)
{
- tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+ tcg_gen_movi_i64(cpu_exclusive_addr, -1);
}
#ifdef CONFIG_USER_ONLY
static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
TCGv_i32 addr, int size)
{
- tcg_gen_mov_i32(cpu_exclusive_test, addr);
+ tcg_gen_extu_i32_i64(cpu_exclusive_test, addr);
tcg_gen_movi_i32(cpu_exclusive_info,
size | (rd << 4) | (rt << 8) | (rt2 << 12));
gen_exception_insn(s, 4, EXCP_STREX);
@@ -6771,6 +6794,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
TCGv_i32 addr, int size)
{
TCGv_i32 tmp;
+ TCGv_i64 val64, extaddr;
int done_label;
int fail_label;
@@ -6782,7 +6806,11 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
} */
fail_label = gen_new_label();
done_label = gen_new_label();
- tcg_gen_brcond_i32(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label);
+ extaddr = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(extaddr, addr);
+ tcg_gen_brcond_i64(TCG_COND_NE, extaddr, cpu_exclusive_addr, fail_label);
+ tcg_temp_free_i64(extaddr);
+
tmp = tcg_temp_new_i32();
switch (size) {
case 0:
@@ -6798,17 +6826,24 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
default:
abort();
}
- tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label);
- tcg_temp_free_i32(tmp);
+
+ val64 = tcg_temp_new_i64();
if (size == 3) {
TCGv_i32 tmp2 = tcg_temp_new_i32();
+ TCGv_i32 tmp3 = tcg_temp_new_i32();
tcg_gen_addi_i32(tmp2, addr, 4);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld32u(tmp, tmp2, IS_USER(s));
+ gen_aa32_ld32u(tmp3, tmp2, IS_USER(s));
tcg_temp_free_i32(tmp2);
- tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label);
- tcg_temp_free_i32(tmp);
+ tcg_gen_concat_i32_i64(val64, tmp, tmp3);
+ tcg_temp_free_i32(tmp3);
+ } else {
+ tcg_gen_extu_i32_i64(val64, tmp);
}
+ tcg_temp_free_i32(tmp);
+
+ tcg_gen_brcond_i64(TCG_COND_NE, val64, cpu_exclusive_val, fail_label);
+ tcg_temp_free_i64(val64);
+
tmp = load_reg(s, rt);
switch (size) {
case 0:
@@ -6836,7 +6871,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
gen_set_label(fail_label);
tcg_gen_movi_i32(cpu_R[rd], 1);
gen_set_label(done_label);
- tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+ tcg_gen_movi_i64(cpu_exclusive_addr, -1);
}
#endif
@@ -7114,7 +7149,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
/* if not always execute, we generate a conditional jump to
next instruction */
s->condlabel = gen_new_label();
- gen_test_cc(cond ^ 1, s->condlabel);
+ arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
}
if ((insn & 0x0f900000) == 0x03000000) {
@@ -9131,7 +9166,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
op = (insn >> 22) & 0xf;
/* Generate a conditional jump to next instruction. */
s->condlabel = gen_new_label();
- gen_test_cc(op ^ 1, s->condlabel);
+ arm_gen_test_cc(op ^ 1, s->condlabel);
s->condjmp = 1;
/* offset[11:1] = insn[10:0] */
@@ -9488,7 +9523,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
cond = s->condexec_cond;
if (cond != 0x0e) { /* Skip conditional when condition is AL. */
s->condlabel = gen_new_label();
- gen_test_cc(cond ^ 1, s->condlabel);
+ arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
}
}
@@ -10161,7 +10196,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s)
}
/* generate a conditional jump to next instruction */
s->condlabel = gen_new_label();
- gen_test_cc(cond ^ 1, s->condlabel);
+ arm_gen_test_cc(cond ^ 1, s->condlabel);
s->condjmp = 1;
/* jump to the offset */
@@ -10217,6 +10252,15 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
int max_insns;
/* generate intermediate code */
+
+ /* The A64 decoder has its own top level loop, because it doesn't need
+ * the A32/T32 complexity to do with conditional execution/IT blocks/etc.
+ */
+ if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
+ gen_intermediate_code_internal_a64(cpu, tb, search_pc);
+ return;
+ }
+
pc_start = tb->pc;
dc->tb = tb;
@@ -10228,31 +10272,20 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
dc->singlestep_enabled = cs->singlestep_enabled;
dc->condjmp = 0;
- if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
- dc->aarch64 = 1;
- dc->thumb = 0;
- dc->bswap_code = 0;
- dc->condexec_mask = 0;
- dc->condexec_cond = 0;
+ dc->aarch64 = 0;
+ dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
+ dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
+ dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
+ dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
#if !defined(CONFIG_USER_ONLY)
- dc->user = 0;
+ dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
#endif
- dc->vfp_enabled = 0;
- dc->vec_len = 0;
- dc->vec_stride = 0;
- } else {
- dc->aarch64 = 0;
- dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
- dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
- dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
- dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
-#if !defined(CONFIG_USER_ONLY)
- dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
-#endif
- dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
- dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
- dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
- }
+ dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
+ dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
+ dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
+ dc->cp_regs = cpu->cp_regs;
+ dc->current_pl = arm_current_pl(env);
+
cpu_F0s = tcg_temp_new_i32();
cpu_F1s = tcg_temp_new_i32();
cpu_F0d = tcg_temp_new_i64();
@@ -10314,7 +10347,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
do {
#ifdef CONFIG_USER_ONLY
/* Intercept jump to the magic kernel page. */
- if (!dc->aarch64 && dc->pc >= 0xffff0000) {
+ if (dc->pc >= 0xffff0000) {
/* We always get here via a jump, so know we are not in a
conditional execution block. */
gen_exception(EXCP_KERNEL_TRAP);
@@ -10362,9 +10395,7 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
tcg_gen_debug_insn_start(dc->pc);
}
- if (dc->aarch64) {
- disas_a64_insn(env, dc);
- } else if (dc->thumb) {
+ if (dc->thumb) {
disas_thumb_insn(env, dc);
if (dc->condexec_mask) {
dc->condexec_cond = (dc->condexec_cond & 0xe)
@@ -10559,8 +10590,9 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, int pc_pos)
{
if (is_a64(env)) {
env->pc = tcg_ctx.gen_opc_pc[pc_pos];
+ env->condexec_bits = 0;
} else {
env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos];
+ env->condexec_bits = gen_opc_condexec_bits[pc_pos];
}
- env->condexec_bits = gen_opc_condexec_bits[pc_pos];
}
diff --git a/target-arm/translate.h b/target-arm/translate.h
index 67c776053b..67da6996c9 100644
--- a/target-arm/translate.h
+++ b/target-arm/translate.h
@@ -24,20 +24,41 @@ typedef struct DisasContext {
int vec_len;
int vec_stride;
int aarch64;
+ int current_pl;
+ GHashTable *cp_regs;
+#define TMP_A64_MAX 16
+ int tmp_a64_count;
+ TCGv_i64 tmp_a64[TMP_A64_MAX];
} DisasContext;
extern TCGv_ptr cpu_env;
+/* target-specific extra values for is_jmp */
+/* These instructions trap after executing, so the A32/T32 decoder must
+ * defer them until after the conditional execution state has been updated.
+ * WFI also needs special handling when single-stepping.
+ */
+#define DISAS_WFI 4
+#define DISAS_SWI 5
+/* For instructions which unconditionally cause an exception we can skip
+ * emitting unreachable code at the end of the TB in the A64 decoder
+ */
+#define DISAS_EXC 6
+
#ifdef TARGET_AARCH64
void a64_translate_init(void);
-void disas_a64_insn(CPUARMState *env, DisasContext *s);
+void gen_intermediate_code_internal_a64(ARMCPU *cpu,
+ TranslationBlock *tb,
+ bool search_pc);
void gen_a64_set_pc_im(uint64_t val);
#else
static inline void a64_translate_init(void)
{
}
-static inline void disas_a64_insn(CPUARMState *env, DisasContext *s)
+static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu,
+ TranslationBlock *tb,
+ bool search_pc)
{
}
@@ -46,4 +67,6 @@ static inline void gen_a64_set_pc_im(uint64_t val)
}
#endif
+void arm_gen_test_cc(int cc, int label);
+
#endif /* TARGET_ARM_TRANSLATE_H */
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index f4fab155bd..d1751a40c6 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -69,6 +69,8 @@ typedef struct X86CPU {
bool hyperv_vapic;
bool hyperv_relaxed_timing;
int hyperv_spinlock_attempts;
+ bool check_cpuid;
+ bool enforce_cpuid;
/* if true the CPUID code directly forward host cache leaves to the guest */
bool cache_info_passthrough;
@@ -82,6 +84,10 @@ typedef struct X86CPU {
* capabilities) directly to the guest.
*/
bool enable_pmu;
+
+ /* in order to simplify APIC support, we leave this pointer to the
+ user */
+ struct DeviceState *apic_state;
} X86CPU;
static inline X86CPU *x86_env_get_cpu(CPUX86State *env)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index bc4cb9d220..e6f7eaf5cd 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -358,9 +358,6 @@ typedef struct model_features_t {
FeatureWord feat_word;
} model_features_t;
-int check_cpuid = 0;
-int enforce_cpuid = 0;
-
static uint32_t kvm_default_features = (1 << KVM_FEATURE_CLOCKSOURCE) |
(1 << KVM_FEATURE_NOP_IO_DELAY) |
(1 << KVM_FEATURE_CLOCKSOURCE2) |
@@ -1600,11 +1597,50 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
error_propagate(errp, err);
}
+static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ X86CPU *cpu = X86_CPU(obj);
+ int64_t value = cpu->hyperv_spinlock_attempts;
+
+ visit_type_int(v, &value, name, errp);
+}
+
+static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ const int64_t min = 0xFFF;
+ const int64_t max = UINT_MAX;
+ X86CPU *cpu = X86_CPU(obj);
+ Error *err = NULL;
+ int64_t value;
+
+ visit_type_int(v, &value, name, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (value < min || value > max) {
+ error_setg(errp, "Property %s.%s doesn't take value %" PRId64
+ " (minimum: %" PRId64 ", maximum: %" PRId64 ")",
+ object_get_typename(obj), name ? name : "null",
+ value, min, max);
+ return;
+ }
+ cpu->hyperv_spinlock_attempts = value;
+}
+
+static PropertyInfo qdev_prop_spinlocks = {
+ .name = "int",
+ .get = x86_get_hv_spinlocks,
+ .set = x86_set_hv_spinlocks,
+};
+
static int cpu_x86_find_by_name(X86CPU *cpu, x86_def_t *x86_cpu_def,
const char *name)
{
x86_def_t *def;
- Error *err = NULL;
int i;
if (name == NULL) {
@@ -1612,8 +1648,7 @@ static int cpu_x86_find_by_name(X86CPU *cpu, x86_def_t *x86_cpu_def,
}
if (kvm_enabled() && strcmp(name, "host") == 0) {
kvm_cpu_fill_host(x86_cpu_def);
- object_property_set_bool(OBJECT(cpu), true, "pmu", &err);
- assert_no_error(err);
+ object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort);
return 0;
}
@@ -1673,15 +1708,7 @@ static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp)
} else if ((val = strchr(featurestr, '='))) {
*val = 0; val++;
feat2prop(featurestr);
- if (!strcmp(featurestr, "family")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
- } else if (!strcmp(featurestr, "model")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
- } else if (!strcmp(featurestr, "stepping")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
- } else if (!strcmp(featurestr, "level")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
- } else if (!strcmp(featurestr, "xlevel")) {
+ if (!strcmp(featurestr, "xlevel")) {
char *err;
char num[32];
@@ -1697,10 +1724,6 @@ static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp)
}
snprintf(num, sizeof(num), "%" PRIu32, numvalue);
object_property_parse(OBJECT(cpu), num, featurestr, errp);
- } else if (!strcmp(featurestr, "vendor")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
- } else if (!strcmp(featurestr, "model-id")) {
- object_property_parse(OBJECT(cpu), val, featurestr, errp);
} else if (!strcmp(featurestr, "tsc-freq")) {
int64_t tsc_freq;
char *err;
@@ -1717,6 +1740,7 @@ static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp)
} else if (!strcmp(featurestr, "hv-spinlocks")) {
char *err;
const int min = 0xFFF;
+ char num[32];
numvalue = strtoul(val, &err, 0);
if (!*val || *err) {
error_setg(errp, "bad numerical value %s", val);
@@ -1728,23 +1752,14 @@ static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp)
min);
numvalue = min;
}
- cpu->hyperv_spinlock_attempts = numvalue;
+ snprintf(num, sizeof(num), "%" PRId32, numvalue);
+ object_property_parse(OBJECT(cpu), num, featurestr, errp);
} else {
- error_setg(errp, "unrecognized feature %s", featurestr);
- goto out;
+ object_property_parse(OBJECT(cpu), val, featurestr, errp);
}
- } else if (!strcmp(featurestr, "check")) {
- check_cpuid = 1;
- } else if (!strcmp(featurestr, "enforce")) {
- check_cpuid = enforce_cpuid = 1;
- } else if (!strcmp(featurestr, "hv_relaxed")) {
- cpu->hyperv_relaxed_timing = true;
- } else if (!strcmp(featurestr, "hv_vapic")) {
- cpu->hyperv_vapic = true;
} else {
- error_setg(errp, "feature string `%s' not in format (+feature|"
- "-feature|feature=xyz)", featurestr);
- goto out;
+ feat2prop(featurestr);
+ object_property_parse(OBJECT(cpu), "on", featurestr, errp);
}
if (error_is_set(errp)) {
goto out;
@@ -2456,7 +2471,7 @@ static void x86_cpu_reset(CPUState *s)
#if !defined(CONFIG_USER_ONLY)
/* We hard-wire the BSP to the first CPU. */
if (s->cpu_index == 0) {
- apic_designate_bsp(env->apic_state);
+ apic_designate_bsp(cpu->apic_state);
}
s->halted = !cpu_is_bsp(cpu);
@@ -2466,7 +2481,7 @@ static void x86_cpu_reset(CPUState *s)
#ifndef CONFIG_USER_ONLY
bool cpu_is_bsp(X86CPU *cpu)
{
- return cpu_get_apic_base(cpu->env.apic_state) & MSR_IA32_APICBASE_BSP;
+ return cpu_get_apic_base(cpu->apic_state) & MSR_IA32_APICBASE_BSP;
}
/* TODO: remove me, when reset over QOM tree is implemented */
@@ -2507,31 +2522,29 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
apic_type = "xen-apic";
}
- env->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type);
- if (env->apic_state == NULL) {
+ cpu->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type);
+ if (cpu->apic_state == NULL) {
error_setg(errp, "APIC device '%s' could not be created", apic_type);
return;
}
object_property_add_child(OBJECT(cpu), "apic",
- OBJECT(env->apic_state), NULL);
- qdev_prop_set_uint8(env->apic_state, "id", env->cpuid_apic_id);
+ OBJECT(cpu->apic_state), NULL);
+ qdev_prop_set_uint8(cpu->apic_state, "id", env->cpuid_apic_id);
/* TODO: convert to link<> */
- apic = APIC_COMMON(env->apic_state);
+ apic = APIC_COMMON(cpu->apic_state);
apic->cpu = cpu;
}
static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
{
- CPUX86State *env = &cpu->env;
-
- if (env->apic_state == NULL) {
+ if (cpu->apic_state == NULL) {
return;
}
- if (qdev_init(env->apic_state)) {
+ if (qdev_init(cpu->apic_state)) {
error_setg(errp, "APIC device '%s' could not be initialized",
- object_get_typename(OBJECT(env->apic_state)));
+ object_get_typename(OBJECT(cpu->apic_state)));
return;
}
}
@@ -2575,8 +2588,8 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
env->features[FEAT_8000_0001_ECX] &= TCG_EXT3_FEATURES;
env->features[FEAT_SVM] &= TCG_SVM_FEATURES;
} else {
- if (check_cpuid && kvm_check_features_against_host(cpu)
- && enforce_cpuid) {
+ if ((cpu->check_cpuid || cpu->enforce_cpuid)
+ && kvm_check_features_against_host(cpu) && cpu->enforce_cpuid) {
error_setg(&local_err,
"Host's CPU doesn't support requested features");
goto out;
@@ -2735,6 +2748,11 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
static Property x86_cpu_properties[] = {
DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
+ { .name = "hv-spinlocks", .info = &qdev_prop_spinlocks },
+ DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false),
+ DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false),
+ DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false),
+ DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index bbec228679..1fcbc82698 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -911,10 +911,6 @@ typedef struct CPUX86State {
int tsc_khz;
void *kvm_xsave_buf;
- /* in order to simplify APIC support, we leave this pointer to the
- user */
- struct DeviceState *apic_state;
-
uint64_t mcg_cap;
uint64_t mcg_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4];
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 7c196ffc42..fe613b26e1 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -531,6 +531,12 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
if (!(env->cr[0] & CR0_PG_MASK)) {
pte = addr;
+#ifdef TARGET_X86_64
+ if (!(env->hflags & HF_LMA_MASK)) {
+ /* Without long mode we can only address 32bits in real mode */
+ pte = (uint32_t)pte;
+ }
+#endif
virt_addr = addr & TARGET_PAGE_MASK;
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
page_size = 4096;
@@ -1241,14 +1247,16 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank,
void cpu_report_tpr_access(CPUX86State *env, TPRAccess access)
{
+ X86CPU *cpu = x86_env_get_cpu(env);
+
if (kvm_enabled()) {
env->tpr_access_type = access;
- cpu_interrupt(CPU(x86_env_get_cpu(env)), CPU_INTERRUPT_TPR);
+ cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TPR);
} else {
cpu_restore_state(env, env->mem_io_pc);
- apic_handle_tpr_access_report(env->apic_state, env->eip, access);
+ apic_handle_tpr_access_report(cpu->apic_state, env->eip, access);
}
}
#endif /* !CONFIG_USER_ONLY */
@@ -1295,14 +1303,12 @@ void do_cpu_init(X86CPU *cpu)
cpu_reset(cs);
cs->interrupt_request = sipi;
env->pat = pat;
- apic_init_reset(env->apic_state);
+ apic_init_reset(cpu->apic_state);
}
void do_cpu_sipi(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
-
- apic_sipi(env->apic_state);
+ apic_sipi(cpu->apic_state);
}
#else
void do_cpu_init(X86CPU *cpu)
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index d34981fd63..0a21c3085d 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -1080,8 +1080,8 @@ static int kvm_put_sregs(X86CPU *cpu)
sregs.cr3 = env->cr[3];
sregs.cr4 = env->cr[4];
- sregs.cr8 = cpu_get_apic_tpr(env->apic_state);
- sregs.apic_base = cpu_get_apic_base(env->apic_state);
+ sregs.cr8 = cpu_get_apic_tpr(cpu->apic_state);
+ sregs.apic_base = cpu_get_apic_base(cpu->apic_state);
sregs.efer = env->efer;
@@ -1651,8 +1651,7 @@ static int kvm_get_mp_state(X86CPU *cpu)
static int kvm_get_apic(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
- DeviceState *apic = env->apic_state;
+ DeviceState *apic = cpu->apic_state;
struct kvm_lapic_state kapic;
int ret;
@@ -1669,8 +1668,7 @@ static int kvm_get_apic(X86CPU *cpu)
static int kvm_put_apic(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
- DeviceState *apic = env->apic_state;
+ DeviceState *apic = cpu->apic_state;
struct kvm_lapic_state kapic;
if (apic && kvm_irqchip_in_kernel()) {
@@ -2001,7 +1999,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run)
}
DPRINTF("setting tpr\n");
- run->cr8 = cpu_get_apic_tpr(env->apic_state);
+ run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state);
}
}
@@ -2015,8 +2013,8 @@ void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
} else {
env->eflags &= ~IF_MASK;
}
- cpu_set_apic_tpr(env->apic_state, run->cr8);
- cpu_set_apic_base(env->apic_state, run->apic_base);
+ cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8);
+ cpu_set_apic_base(x86_cpu->apic_state, run->apic_base);
}
int kvm_arch_process_async_events(CPUState *cs)
@@ -2053,7 +2051,7 @@ int kvm_arch_process_async_events(CPUState *cs)
if (cs->interrupt_request & CPU_INTERRUPT_POLL) {
cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
- apic_poll_irq(env->apic_state);
+ apic_poll_irq(cpu->apic_state);
}
if (((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
(env->eflags & IF_MASK)) ||
@@ -2071,7 +2069,7 @@ int kvm_arch_process_async_events(CPUState *cs)
if (cs->interrupt_request & CPU_INTERRUPT_TPR) {
cs->interrupt_request &= ~CPU_INTERRUPT_TPR;
kvm_cpu_synchronize_state(cs);
- apic_handle_tpr_access_report(env->apic_state, env->eip,
+ apic_handle_tpr_access_report(cpu->apic_state, env->eip,
env->tpr_access_type);
}
@@ -2095,11 +2093,10 @@ static int kvm_handle_halt(X86CPU *cpu)
static int kvm_handle_tpr_access(X86CPU *cpu)
{
- CPUX86State *env = &cpu->env;
CPUState *cs = CPU(cpu);
struct kvm_run *run = cs->kvm_run;
- apic_handle_tpr_access_report(env->apic_state, run->tpr_access.rip,
+ apic_handle_tpr_access_report(cpu->apic_state, run->tpr_access.rip,
run->tpr_access.is_write ? TPR_ACCESS_WRITE
: TPR_ACCESS_READ);
return 1;
diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c
index b6307ca386..47f6a2f7c1 100644
--- a/target-i386/misc_helper.c
+++ b/target-i386/misc_helper.c
@@ -155,7 +155,7 @@ target_ulong helper_read_crN(CPUX86State *env, int reg)
break;
case 8:
if (!(env->hflags2 & HF2_VINTR_MASK)) {
- val = cpu_get_apic_tpr(env->apic_state);
+ val = cpu_get_apic_tpr(x86_env_get_cpu(env)->apic_state);
} else {
val = env->v_tpr;
}
@@ -179,7 +179,7 @@ void helper_write_crN(CPUX86State *env, int reg, target_ulong t0)
break;
case 8:
if (!(env->hflags2 & HF2_VINTR_MASK)) {
- cpu_set_apic_tpr(env->apic_state, t0);
+ cpu_set_apic_tpr(x86_env_get_cpu(env)->apic_state, t0);
}
env->v_tpr = t0 & 0x0f;
break;
@@ -286,7 +286,7 @@ void helper_wrmsr(CPUX86State *env)
env->sysenter_eip = val;
break;
case MSR_IA32_APICBASE:
- cpu_set_apic_base(env->apic_state, val);
+ cpu_set_apic_base(x86_env_get_cpu(env)->apic_state, val);
break;
case MSR_EFER:
{
@@ -437,7 +437,7 @@ void helper_rdmsr(CPUX86State *env)
val = env->sysenter_eip;
break;
case MSR_IA32_APICBASE:
- val = cpu_get_apic_base(env->apic_state);
+ val = cpu_get_apic_base(x86_env_get_cpu(env)->apic_state);
break;
case MSR_EFER:
val = env->efer;
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 7916e5b1f6..b0f227915a 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -85,7 +85,8 @@ typedef struct DisasContext {
/* current insn context */
int override; /* -1 if no override */
int prefix;
- int aflag, dflag;
+ TCGMemOp aflag;
+ TCGMemOp dflag;
target_ulong pc; /* pc = eip + cs_base */
int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
static state change (stop translation) */
@@ -126,7 +127,7 @@ typedef struct DisasContext {
static void gen_eob(DisasContext *s);
static void gen_jmp(DisasContext *s, target_ulong eip);
static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num);
-static void gen_op(DisasContext *s1, int op, int ot, int d);
+static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d);
/* i386 arith/logic operations */
enum {
@@ -163,14 +164,6 @@ enum {
JCC_LE,
};
-/* operand size */
-enum {
- OT_BYTE = 0,
- OT_WORD,
- OT_LONG,
- OT_QUAD,
-};
-
enum {
/* I386 int registers */
OR_EAX, /* MUST be even numbered */
@@ -260,73 +253,6 @@ static void gen_update_cc_op(DisasContext *s)
}
}
-static inline void gen_op_movl_T0_0(void)
-{
- tcg_gen_movi_tl(cpu_T[0], 0);
-}
-
-static inline void gen_op_movl_T0_im(int32_t val)
-{
- tcg_gen_movi_tl(cpu_T[0], val);
-}
-
-static inline void gen_op_movl_T0_imu(uint32_t val)
-{
- tcg_gen_movi_tl(cpu_T[0], val);
-}
-
-static inline void gen_op_movl_T1_im(int32_t val)
-{
- tcg_gen_movi_tl(cpu_T[1], val);
-}
-
-static inline void gen_op_movl_T1_imu(uint32_t val)
-{
- tcg_gen_movi_tl(cpu_T[1], val);
-}
-
-static inline void gen_op_movl_A0_im(uint32_t val)
-{
- tcg_gen_movi_tl(cpu_A0, val);
-}
-
-#ifdef TARGET_X86_64
-static inline void gen_op_movq_A0_im(int64_t val)
-{
- tcg_gen_movi_tl(cpu_A0, val);
-}
-#endif
-
-static inline void gen_movtl_T0_im(target_ulong val)
-{
- tcg_gen_movi_tl(cpu_T[0], val);
-}
-
-static inline void gen_movtl_T1_im(target_ulong val)
-{
- tcg_gen_movi_tl(cpu_T[1], val);
-}
-
-static inline void gen_op_andl_T0_ffff(void)
-{
- tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff);
-}
-
-static inline void gen_op_andl_T0_im(uint32_t val)
-{
- tcg_gen_andi_tl(cpu_T[0], cpu_T[0], val);
-}
-
-static inline void gen_op_movl_T0_T1(void)
-{
- tcg_gen_mov_tl(cpu_T[0], cpu_T[1]);
-}
-
-static inline void gen_op_andl_A0_ffff(void)
-{
- tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffff);
-}
-
#ifdef TARGET_X86_64
#define NB_OP_SIZES 4
@@ -370,66 +296,71 @@ static inline bool byte_reg_is_xH(int reg)
return true;
}
-static inline void gen_op_mov_reg_v(int ot, int reg, TCGv t0)
+/* Select the size of a push/pop operation. */
+static inline TCGMemOp mo_pushpop(DisasContext *s, TCGMemOp ot)
+{
+ if (CODE64(s)) {
+ return ot == MO_16 ? MO_16 : MO_64;
+ } else {
+ return ot;
+ }
+}
+
+/* Select only size 64 else 32. Used for SSE operand sizes. */
+static inline TCGMemOp mo_64_32(TCGMemOp ot)
{
- switch(ot) {
- case OT_BYTE:
- if (!byte_reg_is_xH(reg)) {
- tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8);
- } else {
- tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8);
- }
- break;
- case OT_WORD:
- tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16);
- break;
- default: /* XXX this shouldn't be reached; abort? */
- case OT_LONG:
- /* For x86_64, this sets the higher half of register to zero.
- For i386, this is equivalent to a mov. */
- tcg_gen_ext32u_tl(cpu_regs[reg], t0);
- break;
#ifdef TARGET_X86_64
- case OT_QUAD:
- tcg_gen_mov_tl(cpu_regs[reg], t0);
- break;
+ return ot == MO_64 ? MO_64 : MO_32;
+#else
+ return MO_32;
#endif
- }
}
-static inline void gen_op_mov_reg_T0(int ot, int reg)
+/* Select size 8 if lsb of B is clear, else OT. Used for decoding
+ byte vs word opcodes. */
+static inline TCGMemOp mo_b_d(int b, TCGMemOp ot)
{
- gen_op_mov_reg_v(ot, reg, cpu_T[0]);
+ return b & 1 ? ot : MO_8;
}
-static inline void gen_op_mov_reg_T1(int ot, int reg)
+/* Select size 8 if lsb of B is clear, else OT capped at 32.
+ Used for decoding operand size of port opcodes. */
+static inline TCGMemOp mo_b_d32(int b, TCGMemOp ot)
{
- gen_op_mov_reg_v(ot, reg, cpu_T[1]);
+ return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8;
}
-static inline void gen_op_mov_reg_A0(int size, int reg)
+static void gen_op_mov_reg_v(TCGMemOp ot, int reg, TCGv t0)
{
- switch(size) {
- case OT_BYTE:
- tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_A0, 0, 16);
+ switch(ot) {
+ case MO_8:
+ if (!byte_reg_is_xH(reg)) {
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8);
+ } else {
+ tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8);
+ }
break;
- default: /* XXX this shouldn't be reached; abort? */
- case OT_WORD:
+ case MO_16:
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16);
+ break;
+ case MO_32:
/* For x86_64, this sets the higher half of register to zero.
For i386, this is equivalent to a mov. */
- tcg_gen_ext32u_tl(cpu_regs[reg], cpu_A0);
+ tcg_gen_ext32u_tl(cpu_regs[reg], t0);
break;
#ifdef TARGET_X86_64
- case OT_LONG:
- tcg_gen_mov_tl(cpu_regs[reg], cpu_A0);
+ case MO_64:
+ tcg_gen_mov_tl(cpu_regs[reg], t0);
break;
#endif
+ default:
+ tcg_abort();
}
}
-static inline void gen_op_mov_v_reg(int ot, TCGv t0, int reg)
+static inline void gen_op_mov_v_reg(TCGMemOp ot, TCGv t0, int reg)
{
- if (ot == OT_BYTE && byte_reg_is_xH(reg)) {
+ if (ot == MO_8 && byte_reg_is_xH(reg)) {
tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8);
tcg_gen_ext8u_tl(t0, t0);
} else {
@@ -437,11 +368,6 @@ static inline void gen_op_mov_v_reg(int ot, TCGv t0, int reg)
}
}
-static inline void gen_op_mov_TN_reg(int ot, int t_index, int reg)
-{
- gen_op_mov_v_reg(ot, cpu_T[t_index], reg);
-}
-
static inline void gen_op_movl_A0_reg(int reg)
{
tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]);
@@ -472,58 +398,21 @@ static void gen_add_A0_im(DisasContext *s, int val)
gen_op_addl_A0_im(val);
}
-static inline void gen_op_addl_T0_T1(void)
+static inline void gen_op_jmp_v(TCGv dest)
{
- tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip));
}
-static inline void gen_op_jmp_T0(void)
+static inline void gen_op_add_reg_im(TCGMemOp size, int reg, int32_t val)
{
- tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, eip));
+ tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
+ gen_op_mov_reg_v(size, reg, cpu_tmp0);
}
-static inline void gen_op_add_reg_im(int size, int reg, int32_t val)
+static inline void gen_op_add_reg_T0(TCGMemOp size, int reg)
{
- switch(size) {
- case OT_BYTE:
- tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
- tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16);
- break;
- case OT_WORD:
- tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
- /* For x86_64, this sets the higher half of register to zero.
- For i386, this is equivalent to a nop. */
- tcg_gen_ext32u_tl(cpu_tmp0, cpu_tmp0);
- tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0);
- break;
-#ifdef TARGET_X86_64
- case OT_LONG:
- tcg_gen_addi_tl(cpu_regs[reg], cpu_regs[reg], val);
- break;
-#endif
- }
-}
-
-static inline void gen_op_add_reg_T0(int size, int reg)
-{
- switch(size) {
- case OT_BYTE:
- tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
- tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16);
- break;
- case OT_WORD:
- tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
- /* For x86_64, this sets the higher half of register to zero.
- For i386, this is equivalent to a nop. */
- tcg_gen_ext32u_tl(cpu_tmp0, cpu_tmp0);
- tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0);
- break;
-#ifdef TARGET_X86_64
- case OT_LONG:
- tcg_gen_add_tl(cpu_regs[reg], cpu_regs[reg], cpu_T[0]);
- break;
-#endif
- }
+ tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
+ gen_op_mov_reg_v(size, reg, cpu_tmp0);
}
static inline void gen_op_addl_A0_reg_sN(int shift, int reg)
@@ -584,99 +473,29 @@ static inline void gen_op_addq_A0_reg_sN(int shift, int reg)
}
#endif
-static inline void gen_op_lds_T0_A0(int idx)
+static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0)
{
- int mem_index = (idx >> 2) - 1;
- switch(idx & 3) {
- case OT_BYTE:
- tcg_gen_qemu_ld8s(cpu_T[0], cpu_A0, mem_index);
- break;
- case OT_WORD:
- tcg_gen_qemu_ld16s(cpu_T[0], cpu_A0, mem_index);
- break;
- default:
- case OT_LONG:
- tcg_gen_qemu_ld32s(cpu_T[0], cpu_A0, mem_index);
- break;
- }
+ tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE);
}
-static inline void gen_op_ld_v(int idx, TCGv t0, TCGv a0)
+static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0)
{
- int mem_index = (idx >> 2) - 1;
- switch(idx & 3) {
- case OT_BYTE:
- tcg_gen_qemu_ld8u(t0, a0, mem_index);
- break;
- case OT_WORD:
- tcg_gen_qemu_ld16u(t0, a0, mem_index);
- break;
- case OT_LONG:
- tcg_gen_qemu_ld32u(t0, a0, mem_index);
- break;
- default:
- case OT_QUAD:
- /* Should never happen on 32-bit targets. */
-#ifdef TARGET_X86_64
- tcg_gen_qemu_ld64(t0, a0, mem_index);
-#endif
- break;
- }
+ tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE);
}
-/* XXX: always use ldu or lds */
-static inline void gen_op_ld_T0_A0(int idx)
+static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d)
{
- gen_op_ld_v(idx, cpu_T[0], cpu_A0);
-}
-
-static inline void gen_op_ldu_T0_A0(int idx)
-{
- gen_op_ld_v(idx, cpu_T[0], cpu_A0);
-}
-
-static inline void gen_op_ld_T1_A0(int idx)
-{
- gen_op_ld_v(idx, cpu_T[1], cpu_A0);
-}
-
-static inline void gen_op_st_v(int idx, TCGv t0, TCGv a0)
-{
- int mem_index = (idx >> 2) - 1;
- switch(idx & 3) {
- case OT_BYTE:
- tcg_gen_qemu_st8(t0, a0, mem_index);
- break;
- case OT_WORD:
- tcg_gen_qemu_st16(t0, a0, mem_index);
- break;
- case OT_LONG:
- tcg_gen_qemu_st32(t0, a0, mem_index);
- break;
- default:
- case OT_QUAD:
- /* Should never happen on 32-bit targets. */
-#ifdef TARGET_X86_64
- tcg_gen_qemu_st64(t0, a0, mem_index);
-#endif
- break;
+ if (d == OR_TMP0) {
+ gen_op_st_v(s, idx, cpu_T[0], cpu_A0);
+ } else {
+ gen_op_mov_reg_v(idx, d, cpu_T[0]);
}
}
-static inline void gen_op_st_T0_A0(int idx)
-{
- gen_op_st_v(idx, cpu_T[0], cpu_A0);
-}
-
-static inline void gen_op_st_T1_A0(int idx)
-{
- gen_op_st_v(idx, cpu_T[1], cpu_A0);
-}
-
static inline void gen_jmp_im(target_ulong pc)
{
tcg_gen_movi_tl(cpu_tmp0, pc);
- tcg_gen_st_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, eip));
+ gen_op_jmp_v(cpu_tmp0);
}
static inline void gen_string_movl_A0_ESI(DisasContext *s)
@@ -684,17 +503,18 @@ static inline void gen_string_movl_A0_ESI(DisasContext *s)
int override;
override = s->override;
+ switch (s->aflag) {
#ifdef TARGET_X86_64
- if (s->aflag == 2) {
+ case MO_64:
if (override >= 0) {
gen_op_movq_A0_seg(override);
gen_op_addq_A0_reg_sN(0, R_ESI);
} else {
gen_op_movq_A0_reg(R_ESI);
}
- } else
+ break;
#endif
- if (s->aflag) {
+ case MO_32:
/* 32 bit address */
if (s->addseg && override < 0)
override = R_DS;
@@ -704,54 +524,61 @@ static inline void gen_string_movl_A0_ESI(DisasContext *s)
} else {
gen_op_movl_A0_reg(R_ESI);
}
- } else {
+ break;
+ case MO_16:
/* 16 address, always override */
if (override < 0)
override = R_DS;
- gen_op_movl_A0_reg(R_ESI);
- gen_op_andl_A0_ffff();
+ tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESI]);
gen_op_addl_A0_seg(s, override);
+ break;
+ default:
+ tcg_abort();
}
}
static inline void gen_string_movl_A0_EDI(DisasContext *s)
{
+ switch (s->aflag) {
#ifdef TARGET_X86_64
- if (s->aflag == 2) {
+ case MO_64:
gen_op_movq_A0_reg(R_EDI);
- } else
+ break;
#endif
- if (s->aflag) {
+ case MO_32:
if (s->addseg) {
gen_op_movl_A0_seg(R_ES);
gen_op_addl_A0_reg_sN(0, R_EDI);
} else {
gen_op_movl_A0_reg(R_EDI);
}
- } else {
- gen_op_movl_A0_reg(R_EDI);
- gen_op_andl_A0_ffff();
+ break;
+ case MO_16:
+ tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_EDI]);
gen_op_addl_A0_seg(s, R_ES);
+ break;
+ default:
+ tcg_abort();
}
}
-static inline void gen_op_movl_T0_Dshift(int ot)
+static inline void gen_op_movl_T0_Dshift(TCGMemOp ot)
{
tcg_gen_ld32s_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, df));
tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot);
};
-static TCGv gen_ext_tl(TCGv dst, TCGv src, int size, bool sign)
+static TCGv gen_ext_tl(TCGv dst, TCGv src, TCGMemOp size, bool sign)
{
switch (size) {
- case OT_BYTE:
+ case MO_8:
if (sign) {
tcg_gen_ext8s_tl(dst, src);
} else {
tcg_gen_ext8u_tl(dst, src);
}
return dst;
- case OT_WORD:
+ case MO_16:
if (sign) {
tcg_gen_ext16s_tl(dst, src);
} else {
@@ -759,7 +586,7 @@ static TCGv gen_ext_tl(TCGv dst, TCGv src, int size, bool sign)
}
return dst;
#ifdef TARGET_X86_64
- case OT_LONG:
+ case MO_32:
if (sign) {
tcg_gen_ext32s_tl(dst, src);
} else {
@@ -772,61 +599,65 @@ static TCGv gen_ext_tl(TCGv dst, TCGv src, int size, bool sign)
}
}
-static void gen_extu(int ot, TCGv reg)
+static void gen_extu(TCGMemOp ot, TCGv reg)
{
gen_ext_tl(reg, reg, ot, false);
}
-static void gen_exts(int ot, TCGv reg)
+static void gen_exts(TCGMemOp ot, TCGv reg)
{
gen_ext_tl(reg, reg, ot, true);
}
-static inline void gen_op_jnz_ecx(int size, int label1)
+static inline void gen_op_jnz_ecx(TCGMemOp size, int label1)
{
tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]);
- gen_extu(size + 1, cpu_tmp0);
+ gen_extu(size, cpu_tmp0);
tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, label1);
}
-static inline void gen_op_jz_ecx(int size, int label1)
+static inline void gen_op_jz_ecx(TCGMemOp size, int label1)
{
tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]);
- gen_extu(size + 1, cpu_tmp0);
+ gen_extu(size, cpu_tmp0);
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1);
}
-static void gen_helper_in_func(int ot, TCGv v, TCGv_i32 n)
+static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n)
{
switch (ot) {
- case OT_BYTE:
+ case MO_8:
gen_helper_inb(v, n);
break;
- case OT_WORD:
+ case MO_16:
gen_helper_inw(v, n);
break;
- case OT_LONG:
+ case MO_32:
gen_helper_inl(v, n);
break;
+ default:
+ tcg_abort();
}
}
-static void gen_helper_out_func(int ot, TCGv_i32 v, TCGv_i32 n)
+static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n)
{
switch (ot) {
- case OT_BYTE:
+ case MO_8:
gen_helper_outb(v, n);
break;
- case OT_WORD:
+ case MO_16:
gen_helper_outw(v, n);
break;
- case OT_LONG:
+ case MO_32:
gen_helper_outl(v, n);
break;
+ default:
+ tcg_abort();
}
}
-static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip,
+static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip,
uint32_t svm_flags)
{
int state_saved;
@@ -839,15 +670,17 @@ static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip,
state_saved = 1;
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
switch (ot) {
- case OT_BYTE:
+ case MO_8:
gen_helper_check_iob(cpu_env, cpu_tmp2_i32);
break;
- case OT_WORD:
+ case MO_16:
gen_helper_check_iow(cpu_env, cpu_tmp2_i32);
break;
- case OT_LONG:
+ case MO_32:
gen_helper_check_iol(cpu_env, cpu_tmp2_i32);
break;
+ default:
+ tcg_abort();
}
}
if(s->flags & HF_SVMI_MASK) {
@@ -864,12 +697,12 @@ static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip,
}
}
-static inline void gen_movs(DisasContext *s, int ot)
+static inline void gen_movs(DisasContext *s, TCGMemOp ot)
{
gen_string_movl_A0_ESI(s);
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
gen_string_movl_A0_EDI(s);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_ESI);
gen_op_add_reg_T0(s->aflag, R_EDI);
@@ -1058,7 +891,7 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg)
return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 };
default:
{
- int size = (s->cc_op - CC_OP_ADDB) & 3;
+ TCGMemOp size = (s->cc_op - CC_OP_ADDB) & 3;
TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true);
return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 };
}
@@ -1099,7 +932,7 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 };
default:
{
- int size = (s->cc_op - CC_OP_ADDB) & 3;
+ TCGMemOp size = (s->cc_op - CC_OP_ADDB) & 3;
TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false);
return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 };
}
@@ -1110,7 +943,8 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
value 'b'. In the fast case, T0 is guaranted not to be used. */
static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
{
- int inv, jcc_op, size, cond;
+ int inv, jcc_op, cond;
+ TCGMemOp size;
CCPrepare cc;
TCGv t0;
@@ -1290,37 +1124,37 @@ static int gen_jz_ecx_string(DisasContext *s, target_ulong next_eip)
return l2;
}
-static inline void gen_stos(DisasContext *s, int ot)
+static inline void gen_stos(DisasContext *s, TCGMemOp ot)
{
- gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX);
gen_string_movl_A0_EDI(s);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_EDI);
}
-static inline void gen_lods(DisasContext *s, int ot)
+static inline void gen_lods(DisasContext *s, TCGMemOp ot)
{
gen_string_movl_A0_ESI(s);
- gen_op_ld_T0_A0(ot + s->mem_index);
- gen_op_mov_reg_T0(ot, R_EAX);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_ESI);
}
-static inline void gen_scas(DisasContext *s, int ot)
+static inline void gen_scas(DisasContext *s, TCGMemOp ot)
{
gen_string_movl_A0_EDI(s);
- gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
gen_op(s, OP_CMPL, ot, R_EAX);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_EDI);
}
-static inline void gen_cmps(DisasContext *s, int ot)
+static inline void gen_cmps(DisasContext *s, TCGMemOp ot)
{
gen_string_movl_A0_EDI(s);
- gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
gen_string_movl_A0_ESI(s);
gen_op(s, OP_CMPL, ot, OR_TMP0);
gen_op_movl_T0_Dshift(ot);
@@ -1328,35 +1162,33 @@ static inline void gen_cmps(DisasContext *s, int ot)
gen_op_add_reg_T0(s->aflag, R_EDI);
}
-static inline void gen_ins(DisasContext *s, int ot)
+static inline void gen_ins(DisasContext *s, TCGMemOp ot)
{
if (use_icount)
gen_io_start();
gen_string_movl_A0_EDI(s);
/* Note: we must do this dummy write first to be restartable in
case of page fault. */
- gen_op_movl_T0_0();
- gen_op_st_T0_A0(ot + s->mem_index);
- gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[1]);
+ tcg_gen_movi_tl(cpu_T[0], 0);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]);
tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
gen_helper_in_func(ot, cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
gen_op_movl_T0_Dshift(ot);
gen_op_add_reg_T0(s->aflag, R_EDI);
if (use_icount)
gen_io_end();
}
-static inline void gen_outs(DisasContext *s, int ot)
+static inline void gen_outs(DisasContext *s, TCGMemOp ot)
{
if (use_icount)
gen_io_start();
gen_string_movl_A0_ESI(s);
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
- gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[1]);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]);
tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[0]);
gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32);
@@ -1370,7 +1202,7 @@ static inline void gen_outs(DisasContext *s, int ot)
/* same method as Valgrind : we generate jumps to current or next
instruction */
#define GEN_REPZ(op) \
-static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \
target_ulong cur_eip, target_ulong next_eip) \
{ \
int l2;\
@@ -1386,7 +1218,7 @@ static inline void gen_repz_ ## op(DisasContext *s, int ot, \
}
#define GEN_REPZ2(op) \
-static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \
target_ulong cur_eip, \
target_ulong next_eip, \
int nz) \
@@ -1468,22 +1300,19 @@ static void gen_helper_fp_arith_STN_ST0(int op, int opreg)
}
/* if d == OR_TMP0, it means memory operand (address in A0) */
-static void gen_op(DisasContext *s1, int op, int ot, int d)
+static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
{
if (d != OR_TMP0) {
- gen_op_mov_TN_reg(ot, 0, d);
+ gen_op_mov_v_reg(ot, cpu_T[0], d);
} else {
- gen_op_ld_T0_A0(ot + s1->mem_index);
+ gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0);
}
switch(op) {
case OP_ADCL:
gen_compute_eflags_c(s1, cpu_tmp4);
tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_tmp4);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update3_cc(cpu_tmp4);
set_cc_op(s1, CC_OP_ADCB + ot);
break;
@@ -1491,57 +1320,39 @@ static void gen_op(DisasContext *s1, int op, int ot, int d)
gen_compute_eflags_c(s1, cpu_tmp4);
tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_tmp4);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update3_cc(cpu_tmp4);
set_cc_op(s1, CC_OP_SBBB + ot);
break;
case OP_ADDL:
- gen_op_addl_T0_T1();
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update2_cc();
set_cc_op(s1, CC_OP_ADDB + ot);
break;
case OP_SUBL:
tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]);
tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update2_cc();
set_cc_op(s1, CC_OP_SUBB + ot);
break;
default:
case OP_ANDL:
tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update1_cc();
set_cc_op(s1, CC_OP_LOGICB + ot);
break;
case OP_ORL:
tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update1_cc();
set_cc_op(s1, CC_OP_LOGICB + ot);
break;
case OP_XORL:
tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
gen_op_update1_cc();
set_cc_op(s1, CC_OP_LOGICB + ot);
break;
@@ -1555,12 +1366,13 @@ static void gen_op(DisasContext *s1, int op, int ot, int d)
}
/* if d == OR_TMP0, it means memory operand (address in A0) */
-static void gen_inc(DisasContext *s1, int ot, int d, int c)
+static void gen_inc(DisasContext *s1, TCGMemOp ot, int d, int c)
{
- if (d != OR_TMP0)
- gen_op_mov_TN_reg(ot, 0, d);
- else
- gen_op_ld_T0_A0(ot + s1->mem_index);
+ if (d != OR_TMP0) {
+ gen_op_mov_v_reg(ot, cpu_T[0], d);
+ } else {
+ gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0);
+ }
gen_compute_eflags_c(s1, cpu_cc_src);
if (c > 0) {
tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1);
@@ -1569,15 +1381,12 @@ static void gen_inc(DisasContext *s1, int ot, int d, int c)
tcg_gen_addi_tl(cpu_T[0], cpu_T[0], -1);
set_cc_op(s1, CC_OP_DECB + ot);
}
- if (d != OR_TMP0)
- gen_op_mov_reg_T0(ot, d);
- else
- gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_st_rm_T0_A0(s1, ot, d);
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
}
-static void gen_shift_flags(DisasContext *s, int ot, TCGv result, TCGv shm1,
- TCGv count, bool is_right)
+static void gen_shift_flags(DisasContext *s, TCGMemOp ot, TCGv result,
+ TCGv shm1, TCGv count, bool is_right)
{
TCGv_i32 z32, s32, oldop;
TCGv z_tl;
@@ -1621,16 +1430,16 @@ static void gen_shift_flags(DisasContext *s, int ot, TCGv result, TCGv shm1,
set_cc_op(s, CC_OP_DYNAMIC);
}
-static void gen_shift_rm_T1(DisasContext *s, int ot, int op1,
+static void gen_shift_rm_T1(DisasContext *s, TCGMemOp ot, int op1,
int is_right, int is_arith)
{
- target_ulong mask = (ot == OT_QUAD ? 0x3f : 0x1f);
+ target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f);
/* load */
if (op1 == OR_TMP0) {
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
}
tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask);
@@ -1652,25 +1461,21 @@ static void gen_shift_rm_T1(DisasContext *s, int ot, int op1,
}
/* store */
- if (op1 == OR_TMP0) {
- gen_op_st_T0_A0(ot + s->mem_index);
- } else {
- gen_op_mov_reg_T0(ot, op1);
- }
+ gen_op_st_rm_T0_A0(s, ot, op1);
gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, cpu_T[1], is_right);
}
-static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2,
+static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2,
int is_right, int is_arith)
{
- int mask = (ot == OT_QUAD ? 0x3f : 0x1f);
+ int mask = (ot == MO_64 ? 0x3f : 0x1f);
/* load */
if (op1 == OR_TMP0)
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
else
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
op2 &= mask;
if (op2 != 0) {
@@ -1691,11 +1496,8 @@ static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2,
}
/* store */
- if (op1 == OR_TMP0)
- gen_op_st_T0_A0(ot + s->mem_index);
- else
- gen_op_mov_reg_T0(ot, op1);
-
+ gen_op_st_rm_T0_A0(s, ot, op1);
+
/* update eflags if non zero shift */
if (op2 != 0) {
tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4);
@@ -1712,33 +1514,33 @@ static inline void tcg_gen_lshift(TCGv ret, TCGv arg1, target_long arg2)
tcg_gen_shri_tl(ret, arg1, -arg2);
}
-static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, int is_right)
+static void gen_rot_rm_T1(DisasContext *s, TCGMemOp ot, int op1, int is_right)
{
- target_ulong mask = (ot == OT_QUAD ? 0x3f : 0x1f);
+ target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f);
TCGv_i32 t0, t1;
/* load */
if (op1 == OR_TMP0) {
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
}
tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask);
switch (ot) {
- case OT_BYTE:
+ case MO_8:
/* Replicate the 8-bit input so that a 32-bit rotate works. */
tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]);
tcg_gen_muli_tl(cpu_T[0], cpu_T[0], 0x01010101);
goto do_long;
- case OT_WORD:
+ case MO_16:
/* Replicate the 16-bit input so that a 32-bit rotate works. */
tcg_gen_deposit_tl(cpu_T[0], cpu_T[0], cpu_T[0], 16, 16);
goto do_long;
do_long:
#ifdef TARGET_X86_64
- case OT_LONG:
+ case MO_32:
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
if (is_right) {
@@ -1759,11 +1561,7 @@ static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, int is_right)
}
/* store */
- if (op1 == OR_TMP0) {
- gen_op_st_T0_A0(ot + s->mem_index);
- } else {
- gen_op_mov_reg_T0(ot, op1);
- }
+ gen_op_st_rm_T0_A0(s, ot, op1);
/* We'll need the flags computed into CC_SRC. */
gen_compute_eflags(s);
@@ -1801,24 +1599,24 @@ static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, int is_right)
set_cc_op(s, CC_OP_DYNAMIC);
}
-static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2,
+static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2,
int is_right)
{
- int mask = (ot == OT_QUAD ? 0x3f : 0x1f);
+ int mask = (ot == MO_64 ? 0x3f : 0x1f);
int shift;
/* load */
if (op1 == OR_TMP0) {
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
}
op2 &= mask;
if (op2 != 0) {
switch (ot) {
#ifdef TARGET_X86_64
- case OT_LONG:
+ case MO_32:
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
if (is_right) {
tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2);
@@ -1835,10 +1633,10 @@ static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2,
tcg_gen_rotli_tl(cpu_T[0], cpu_T[0], op2);
}
break;
- case OT_BYTE:
+ case MO_8:
mask = 7;
goto do_shifts;
- case OT_WORD:
+ case MO_16:
mask = 15;
do_shifts:
shift = op2 & mask;
@@ -1854,11 +1652,7 @@ static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2,
}
/* store */
- if (op1 == OR_TMP0) {
- gen_op_st_T0_A0(ot + s->mem_index);
- } else {
- gen_op_mov_reg_T0(ot, op1);
- }
+ gen_op_st_rm_T0_A0(s, ot, op1);
if (op2 != 0) {
/* Compute the flags into CC_SRC. */
@@ -1883,7 +1677,7 @@ static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2,
}
/* XXX: add faster immediate = 1 case */
-static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1,
+static void gen_rotc_rm_T1(DisasContext *s, TCGMemOp ot, int op1,
int is_right)
{
gen_compute_eflags(s);
@@ -1891,71 +1685,72 @@ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1,
/* load */
if (op1 == OR_TMP0)
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
else
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
if (is_right) {
switch (ot) {
- case OT_BYTE:
+ case MO_8:
gen_helper_rcrb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
- case OT_WORD:
+ case MO_16:
gen_helper_rcrw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
- case OT_LONG:
+ case MO_32:
gen_helper_rcrl(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
gen_helper_rcrq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
#endif
+ default:
+ tcg_abort();
}
} else {
switch (ot) {
- case OT_BYTE:
+ case MO_8:
gen_helper_rclb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
- case OT_WORD:
+ case MO_16:
gen_helper_rclw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
- case OT_LONG:
+ case MO_32:
gen_helper_rcll(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
gen_helper_rclq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]);
break;
#endif
+ default:
+ tcg_abort();
}
}
/* store */
- if (op1 == OR_TMP0)
- gen_op_st_T0_A0(ot + s->mem_index);
- else
- gen_op_mov_reg_T0(ot, op1);
+ gen_op_st_rm_T0_A0(s, ot, op1);
}
/* XXX: add faster immediate case */
-static void gen_shiftd_rm_T1(DisasContext *s, int ot, int op1,
+static void gen_shiftd_rm_T1(DisasContext *s, TCGMemOp ot, int op1,
bool is_right, TCGv count_in)
{
- target_ulong mask = (ot == OT_QUAD ? 63 : 31);
+ target_ulong mask = (ot == MO_64 ? 63 : 31);
TCGv count;
/* load */
if (op1 == OR_TMP0) {
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, op1);
+ gen_op_mov_v_reg(ot, cpu_T[0], op1);
}
count = tcg_temp_new();
tcg_gen_andi_tl(count, count_in, mask);
switch (ot) {
- case OT_WORD:
+ case MO_16:
/* Note: we implement the Intel behaviour for shift count > 16.
This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A
portion by constructing it as a 32-bit value. */
@@ -1968,7 +1763,7 @@ static void gen_shiftd_rm_T1(DisasContext *s, int ot, int op1,
}
/* FALLTHRU */
#ifdef TARGET_X86_64
- case OT_LONG:
+ case MO_32:
/* Concatenate the two 32-bit values and use a 64-bit shift. */
tcg_gen_subi_tl(cpu_tmp0, count, 1);
if (is_right) {
@@ -1994,7 +1789,7 @@ static void gen_shiftd_rm_T1(DisasContext *s, int ot, int op1,
tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_tmp4);
} else {
tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0);
- if (ot == OT_WORD) {
+ if (ot == MO_16) {
/* Only needed if count > 16, for Intel behaviour. */
tcg_gen_subfi_tl(cpu_tmp4, 33, count);
tcg_gen_shr_tl(cpu_tmp4, cpu_T[1], cpu_tmp4);
@@ -2013,20 +1808,16 @@ static void gen_shiftd_rm_T1(DisasContext *s, int ot, int op1,
}
/* store */
- if (op1 == OR_TMP0) {
- gen_op_st_T0_A0(ot + s->mem_index);
- } else {
- gen_op_mov_reg_T0(ot, op1);
- }
+ gen_op_st_rm_T0_A0(s, ot, op1);
gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, count, is_right);
tcg_temp_free(count);
}
-static void gen_shift(DisasContext *s1, int op, int ot, int d, int s)
+static void gen_shift(DisasContext *s1, int op, TCGMemOp ot, int d, int s)
{
if (s != OR_TMP1)
- gen_op_mov_TN_reg(ot, 1, s);
+ gen_op_mov_v_reg(ot, cpu_T[1], s);
switch(op) {
case OP_ROL:
gen_rot_rm_T1(s1, ot, d, 0);
@@ -2053,7 +1844,7 @@ static void gen_shift(DisasContext *s1, int op, int ot, int d, int s)
}
}
-static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c)
+static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c)
{
switch(op) {
case OP_ROL:
@@ -2074,21 +1865,19 @@ static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c)
break;
default:
/* currently not optimized */
- gen_op_movl_T1_im(c);
+ tcg_gen_movi_tl(cpu_T[1], c);
gen_shift(s1, op, ot, d, OR_TMP1);
break;
}
}
-static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm,
- int *reg_ptr, int *offset_ptr)
+static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm)
{
target_long disp;
int havesib;
int base;
int index;
int scale;
- int opreg;
int mod, rm, code, override, must_add_seg;
TCGv sum;
@@ -2099,7 +1888,9 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm,
mod = (modrm >> 6) & 3;
rm = modrm & 7;
- if (s->aflag) {
+ switch (s->aflag) {
+ case MO_64:
+ case MO_32:
havesib = 0;
base = rm;
index = -1;
@@ -2179,26 +1970,28 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm,
tcg_gen_ld_tl(cpu_tmp0, cpu_env,
offsetof(CPUX86State, segs[override].base));
if (CODE64(s)) {
- if (s->aflag != 2) {
+ if (s->aflag == MO_32) {
tcg_gen_ext32u_tl(cpu_A0, cpu_A0);
}
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
- goto done;
+ return;
}
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
}
- if (s->aflag != 2) {
+ if (s->aflag == MO_32) {
tcg_gen_ext32u_tl(cpu_A0, cpu_A0);
}
- } else {
+ break;
+
+ case MO_16:
switch (mod) {
case 0:
if (rm == 6) {
disp = cpu_lduw_code(env, s->pc);
s->pc += 2;
- gen_op_movl_A0_im(disp);
+ tcg_gen_movi_tl(cpu_A0, disp);
rm = 0; /* avoid SS override */
goto no_rm;
} else {
@@ -2210,61 +2003,57 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm,
break;
default:
case 2:
- disp = cpu_lduw_code(env, s->pc);
+ disp = (int16_t)cpu_lduw_code(env, s->pc);
s->pc += 2;
break;
}
- switch(rm) {
+
+ sum = cpu_A0;
+ switch (rm) {
case 0:
- gen_op_movl_A0_reg(R_EBX);
- gen_op_addl_A0_reg_sN(0, R_ESI);
+ tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_ESI]);
break;
case 1:
- gen_op_movl_A0_reg(R_EBX);
- gen_op_addl_A0_reg_sN(0, R_EDI);
+ tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_EDI]);
break;
case 2:
- gen_op_movl_A0_reg(R_EBP);
- gen_op_addl_A0_reg_sN(0, R_ESI);
+ tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_ESI]);
break;
case 3:
- gen_op_movl_A0_reg(R_EBP);
- gen_op_addl_A0_reg_sN(0, R_EDI);
+ tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_EDI]);
break;
case 4:
- gen_op_movl_A0_reg(R_ESI);
+ sum = cpu_regs[R_ESI];
break;
case 5:
- gen_op_movl_A0_reg(R_EDI);
+ sum = cpu_regs[R_EDI];
break;
case 6:
- gen_op_movl_A0_reg(R_EBP);
+ sum = cpu_regs[R_EBP];
break;
default:
case 7:
- gen_op_movl_A0_reg(R_EBX);
+ sum = cpu_regs[R_EBX];
break;
}
- if (disp != 0)
- gen_op_addl_A0_im(disp);
- gen_op_andl_A0_ffff();
+ tcg_gen_addi_tl(cpu_A0, sum, disp);
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
no_rm:
if (must_add_seg) {
if (override < 0) {
- if (rm == 2 || rm == 3 || rm == 6)
+ if (rm == 2 || rm == 3 || rm == 6) {
override = R_SS;
- else
+ } else {
override = R_DS;
+ }
}
gen_op_addl_A0_seg(s, override);
}
- }
+ break;
- done:
- opreg = OR_A0;
- disp = 0;
- *reg_ptr = opreg;
- *offset_ptr = disp;
+ default:
+ tcg_abort();
+ }
}
static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm)
@@ -2276,8 +2065,9 @@ static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm)
return;
rm = modrm & 7;
- if (s->aflag) {
-
+ switch (s->aflag) {
+ case MO_64:
+ case MO_32:
base = rm;
if (base == 4) {
@@ -2299,7 +2089,9 @@ static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm)
s->pc += 4;
break;
}
- } else {
+ break;
+
+ case MO_16:
switch (mod) {
case 0:
if (rm == 6) {
@@ -2314,6 +2106,10 @@ static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm)
s->pc += 2;
break;
}
+ break;
+
+ default:
+ tcg_abort();
}
}
@@ -2342,64 +2138,69 @@ static void gen_add_A0_ds_seg(DisasContext *s)
/* generate modrm memory load or store of 'reg'. TMP0 is used if reg ==
OR_TMP0 */
static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm,
- int ot, int reg, int is_store)
+ TCGMemOp ot, int reg, int is_store)
{
- int mod, rm, opreg, disp;
+ int mod, rm;
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
if (mod == 3) {
if (is_store) {
if (reg != OR_TMP0)
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
} else {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
if (reg != OR_TMP0)
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
}
} else {
- gen_lea_modrm(env, s, modrm, &opreg, &disp);
+ gen_lea_modrm(env, s, modrm);
if (is_store) {
if (reg != OR_TMP0)
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
if (reg != OR_TMP0)
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
}
}
}
-static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, int ot)
+static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, TCGMemOp ot)
{
uint32_t ret;
- switch(ot) {
- case OT_BYTE:
+ switch (ot) {
+ case MO_8:
ret = cpu_ldub_code(env, s->pc);
s->pc++;
break;
- case OT_WORD:
+ case MO_16:
ret = cpu_lduw_code(env, s->pc);
s->pc += 2;
break;
- default:
- case OT_LONG:
+ case MO_32:
+#ifdef TARGET_X86_64
+ case MO_64:
+#endif
ret = cpu_ldl_code(env, s->pc);
s->pc += 4;
break;
+ default:
+ tcg_abort();
}
return ret;
}
-static inline int insn_const_size(unsigned int ot)
+static inline int insn_const_size(TCGMemOp ot)
{
- if (ot <= OT_LONG)
+ if (ot <= MO_32) {
return 1 << ot;
- else
+ } else {
return 4;
+ }
}
static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip)
@@ -2452,7 +2253,7 @@ static inline void gen_jcc(DisasContext *s, int b,
}
}
-static void gen_cmovcc1(CPUX86State *env, DisasContext *s, int ot, int b,
+static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b,
int modrm, int reg)
{
CCPrepare cc;
@@ -2471,7 +2272,7 @@ static void gen_cmovcc1(CPUX86State *env, DisasContext *s, int ot, int b,
tcg_gen_movcond_tl(cc.cond, cpu_T[0], cc.reg, cc.reg2,
cpu_T[0], cpu_regs[reg]);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
if (cc.mask != -1) {
tcg_temp_free(cc.reg);
@@ -2548,135 +2349,80 @@ static inline void gen_stack_update(DisasContext *s, int addend)
{
#ifdef TARGET_X86_64
if (CODE64(s)) {
- gen_op_add_reg_im(2, R_ESP, addend);
+ gen_op_add_reg_im(MO_64, R_ESP, addend);
} else
#endif
if (s->ss32) {
- gen_op_add_reg_im(1, R_ESP, addend);
+ gen_op_add_reg_im(MO_32, R_ESP, addend);
} else {
- gen_op_add_reg_im(0, R_ESP, addend);
+ gen_op_add_reg_im(MO_16, R_ESP, addend);
}
}
-/* generate a push. It depends on ss32, addseg and dflag */
-static void gen_push_T0(DisasContext *s)
+/* Generate a push. It depends on ss32, addseg and dflag. */
+static void gen_push_v(DisasContext *s, TCGv val)
{
-#ifdef TARGET_X86_64
- if (CODE64(s)) {
- gen_op_movq_A0_reg(R_ESP);
- if (s->dflag) {
- gen_op_addq_A0_im(-8);
- gen_op_st_T0_A0(OT_QUAD + s->mem_index);
- } else {
- gen_op_addq_A0_im(-2);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
- }
- gen_op_mov_reg_A0(2, R_ESP);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_ESP);
- if (!s->dflag)
- gen_op_addl_A0_im(-2);
- else
- gen_op_addl_A0_im(-4);
- if (s->ss32) {
- if (s->addseg) {
- tcg_gen_mov_tl(cpu_T[1], cpu_A0);
- gen_op_addl_A0_seg(s, R_SS);
- }
- } else {
- gen_op_andl_A0_ffff();
- tcg_gen_mov_tl(cpu_T[1], cpu_A0);
- gen_op_addl_A0_seg(s, R_SS);
- }
- gen_op_st_T0_A0(s->dflag + 1 + s->mem_index);
- if (s->ss32 && !s->addseg)
- gen_op_mov_reg_A0(1, R_ESP);
- else
- gen_op_mov_reg_T1(s->ss32 + 1, R_ESP);
- }
-}
+ TCGMemOp a_ot, d_ot = mo_pushpop(s, s->dflag);
+ int size = 1 << d_ot;
+ TCGv new_esp = cpu_A0;
+
+ tcg_gen_subi_tl(cpu_A0, cpu_regs[R_ESP], size);
-/* generate a push. It depends on ss32, addseg and dflag */
-/* slower version for T1, only used for call Ev */
-static void gen_push_T1(DisasContext *s)
-{
-#ifdef TARGET_X86_64
if (CODE64(s)) {
- gen_op_movq_A0_reg(R_ESP);
- if (s->dflag) {
- gen_op_addq_A0_im(-8);
- gen_op_st_T1_A0(OT_QUAD + s->mem_index);
- } else {
- gen_op_addq_A0_im(-2);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
- }
- gen_op_mov_reg_A0(2, R_ESP);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_ESP);
- if (!s->dflag)
- gen_op_addl_A0_im(-2);
- else
- gen_op_addl_A0_im(-4);
- if (s->ss32) {
- if (s->addseg) {
- gen_op_addl_A0_seg(s, R_SS);
- }
- } else {
- gen_op_andl_A0_ffff();
+ a_ot = MO_64;
+ } else if (s->ss32) {
+ a_ot = MO_32;
+ if (s->addseg) {
+ new_esp = cpu_tmp4;
+ tcg_gen_mov_tl(new_esp, cpu_A0);
gen_op_addl_A0_seg(s, R_SS);
+ } else {
+ tcg_gen_ext32u_tl(cpu_A0, cpu_A0);
}
- gen_op_st_T1_A0(s->dflag + 1 + s->mem_index);
-
- if (s->ss32 && !s->addseg)
- gen_op_mov_reg_A0(1, R_ESP);
- else
- gen_stack_update(s, (-2) << s->dflag);
+ } else {
+ a_ot = MO_16;
+ new_esp = cpu_tmp4;
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
+ tcg_gen_mov_tl(new_esp, cpu_A0);
+ gen_op_addl_A0_seg(s, R_SS);
}
+
+ gen_op_st_v(s, d_ot, val, cpu_A0);
+ gen_op_mov_reg_v(a_ot, R_ESP, new_esp);
}
/* two step pop is necessary for precise exceptions */
-static void gen_pop_T0(DisasContext *s)
+static TCGMemOp gen_pop_T0(DisasContext *s)
{
-#ifdef TARGET_X86_64
+ TCGMemOp d_ot = mo_pushpop(s, s->dflag);
+ TCGv addr = cpu_A0;
+
if (CODE64(s)) {
- gen_op_movq_A0_reg(R_ESP);
- gen_op_ld_T0_A0((s->dflag ? OT_QUAD : OT_WORD) + s->mem_index);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_ESP);
- if (s->ss32) {
- if (s->addseg)
- gen_op_addl_A0_seg(s, R_SS);
- } else {
- gen_op_andl_A0_ffff();
- gen_op_addl_A0_seg(s, R_SS);
- }
- gen_op_ld_T0_A0(s->dflag + 1 + s->mem_index);
+ addr = cpu_regs[R_ESP];
+ } else if (!s->ss32) {
+ tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESP]);
+ gen_op_addl_A0_seg(s, R_SS);
+ } else if (s->addseg) {
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[R_ESP]);
+ gen_op_addl_A0_seg(s, R_SS);
+ } else {
+ tcg_gen_ext32u_tl(cpu_A0, cpu_regs[R_ESP]);
}
+
+ gen_op_ld_v(s, d_ot, cpu_T[0], addr);
+ return d_ot;
}
-static void gen_pop_update(DisasContext *s)
+static void gen_pop_update(DisasContext *s, TCGMemOp ot)
{
-#ifdef TARGET_X86_64
- if (CODE64(s) && s->dflag) {
- gen_stack_update(s, 8);
- } else
-#endif
- {
- gen_stack_update(s, 2 << s->dflag);
- }
+ gen_stack_update(s, 1 << ot);
}
static void gen_stack_A0(DisasContext *s)
{
gen_op_movl_A0_reg(R_ESP);
if (!s->ss32)
- gen_op_andl_A0_ffff();
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
tcg_gen_mov_tl(cpu_T[1], cpu_A0);
if (s->addseg)
gen_op_addl_A0_seg(s, R_SS);
@@ -2687,18 +2433,18 @@ static void gen_pusha(DisasContext *s)
{
int i;
gen_op_movl_A0_reg(R_ESP);
- gen_op_addl_A0_im(-16 << s->dflag);
+ gen_op_addl_A0_im(-8 << s->dflag);
if (!s->ss32)
- gen_op_andl_A0_ffff();
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
tcg_gen_mov_tl(cpu_T[1], cpu_A0);
if (s->addseg)
gen_op_addl_A0_seg(s, R_SS);
for(i = 0;i < 8; i++) {
- gen_op_mov_TN_reg(OT_LONG, 0, 7 - i);
- gen_op_st_T0_A0(OT_WORD + s->dflag + s->mem_index);
- gen_op_addl_A0_im(2 << s->dflag);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], 7 - i);
+ gen_op_st_v(s, s->dflag, cpu_T[0], cpu_A0);
+ gen_op_addl_A0_im(1 << s->dflag);
}
- gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+ gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]);
}
/* NOTE: wrap around in 16 bit not fully handled */
@@ -2707,73 +2453,68 @@ static void gen_popa(DisasContext *s)
int i;
gen_op_movl_A0_reg(R_ESP);
if (!s->ss32)
- gen_op_andl_A0_ffff();
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
tcg_gen_mov_tl(cpu_T[1], cpu_A0);
- tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 16 << s->dflag);
+ tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 8 << s->dflag);
if (s->addseg)
gen_op_addl_A0_seg(s, R_SS);
for(i = 0;i < 8; i++) {
/* ESP is not reloaded */
if (i != 3) {
- gen_op_ld_T0_A0(OT_WORD + s->dflag + s->mem_index);
- gen_op_mov_reg_T0(OT_WORD + s->dflag, 7 - i);
+ gen_op_ld_v(s, s->dflag, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(s->dflag, 7 - i, cpu_T[0]);
}
- gen_op_addl_A0_im(2 << s->dflag);
+ gen_op_addl_A0_im(1 << s->dflag);
}
- gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+ gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]);
}
static void gen_enter(DisasContext *s, int esp_addend, int level)
{
- int ot, opsize;
+ TCGMemOp ot = mo_pushpop(s, s->dflag);
+ int opsize = 1 << ot;
level &= 0x1f;
#ifdef TARGET_X86_64
if (CODE64(s)) {
- ot = s->dflag ? OT_QUAD : OT_WORD;
- opsize = 1 << ot;
-
gen_op_movl_A0_reg(R_ESP);
gen_op_addq_A0_im(-opsize);
tcg_gen_mov_tl(cpu_T[1], cpu_A0);
/* push bp */
- gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
if (level) {
/* XXX: must save state */
gen_helper_enter64_level(cpu_env, tcg_const_i32(level),
- tcg_const_i32((ot == OT_QUAD)),
+ tcg_const_i32((ot == MO_64)),
cpu_T[1]);
}
- gen_op_mov_reg_T1(ot, R_EBP);
+ gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]);
tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level));
- gen_op_mov_reg_T1(OT_QUAD, R_ESP);
+ gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[1]);
} else
#endif
{
- ot = s->dflag + OT_WORD;
- opsize = 2 << s->dflag;
-
gen_op_movl_A0_reg(R_ESP);
gen_op_addl_A0_im(-opsize);
if (!s->ss32)
- gen_op_andl_A0_ffff();
+ tcg_gen_ext16u_tl(cpu_A0, cpu_A0);
tcg_gen_mov_tl(cpu_T[1], cpu_A0);
if (s->addseg)
gen_op_addl_A0_seg(s, R_SS);
/* push bp */
- gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
if (level) {
/* XXX: must save state */
gen_helper_enter_level(cpu_env, tcg_const_i32(level),
- tcg_const_i32(s->dflag),
+ tcg_const_i32(s->dflag - 1),
cpu_T[1]);
}
- gen_op_mov_reg_T1(ot, R_EBP);
+ gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]);
tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level));
- gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+ gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]);
}
}
@@ -2846,38 +2587,36 @@ static void gen_jmp(DisasContext *s, target_ulong eip)
gen_jmp_tb(s, eip, 0);
}
-static inline void gen_ldq_env_A0(int idx, int offset)
+static inline void gen_ldq_env_A0(DisasContext *s, int offset)
{
- int mem_index = (idx >> 2) - 1;
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ);
tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset);
}
-static inline void gen_stq_env_A0(int idx, int offset)
+static inline void gen_stq_env_A0(DisasContext *s, int offset)
{
- int mem_index = (idx >> 2) - 1;
tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset);
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ);
}
-static inline void gen_ldo_env_A0(int idx, int offset)
+static inline void gen_ldo_env_A0(DisasContext *s, int offset)
{
- int mem_index = (idx >> 2) - 1;
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, mem_index);
+ int mem_index = s->mem_index;
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ);
tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0)));
tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8);
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_tmp0, mem_index);
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ);
tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1)));
}
-static inline void gen_sto_env_A0(int idx, int offset)
+static inline void gen_sto_env_A0(DisasContext *s, int offset)
{
- int mem_index = (idx >> 2) - 1;
+ int mem_index = s->mem_index;
tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0)));
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ);
tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8);
tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1)));
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_tmp0, mem_index);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ);
}
static inline void gen_op_movo(int d_offset, int s_offset)
@@ -3239,12 +2978,13 @@ static const struct SSEOpHelper_eppi sse_op_table7[256] = {
static void gen_sse(CPUX86State *env, DisasContext *s, int b,
target_ulong pc_start, int rex_r)
{
- int b1, op1_offset, op2_offset, is_xmm, val, ot;
- int modrm, mod, rm, reg, reg_addr, offset_addr;
+ int b1, op1_offset, op2_offset, is_xmm, val;
+ int modrm, mod, rm, reg;
SSEFunc_0_epp sse_fn_epp;
SSEFunc_0_eppi sse_fn_eppi;
SSEFunc_0_ppi sse_fn_ppi;
SSEFunc_0_eppt sse_fn_eppt;
+ TCGMemOp ot;
b &= 0xff;
if (s->prefix & PREFIX_DATA)
@@ -3311,46 +3051,45 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x0e7: /* movntq */
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx));
break;
case 0x1e7: /* movntdq */
case 0x02b: /* movntps */
case 0x12b: /* movntps */
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
break;
case 0x3f0: /* lddqu */
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
break;
case 0x22b: /* movntss */
case 0x32b: /* movntsd */
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
if (b1 & 1) {
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,
- xmm_regs[reg]));
+ gen_stq_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
} else {
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
xmm_regs[reg].XMM_L(0)));
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0);
}
break;
case 0x6e: /* movd mm, ea */
#ifdef TARGET_X86_64
- if (s->dflag == 2) {
- gen_ldst_modrm(env, s, modrm, OT_QUAD, OR_TMP0, 0);
+ if (s->dflag == MO_64) {
+ gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0);
tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx));
} else
#endif
{
- gen_ldst_modrm(env, s, modrm, OT_LONG, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
offsetof(CPUX86State,fpregs[reg].mmx));
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
@@ -3359,15 +3098,15 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x16e: /* movd xmm, ea */
#ifdef TARGET_X86_64
- if (s->dflag == 2) {
- gen_ldst_modrm(env, s, modrm, OT_QUAD, OR_TMP0, 0);
+ if (s->dflag == MO_64) {
+ gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
offsetof(CPUX86State,xmm_regs[reg]));
gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T[0]);
} else
#endif
{
- gen_ldst_modrm(env, s, modrm, OT_LONG, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
offsetof(CPUX86State,xmm_regs[reg]));
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
@@ -3376,8 +3115,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x6f: /* movq mm, ea */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx));
} else {
rm = (modrm & 7);
tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env,
@@ -3393,8 +3132,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x16f: /* movdqa xmm, ea */
case 0x26f: /* movdqu xmm, ea */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movo(offsetof(CPUX86State,xmm_regs[reg]),
@@ -3403,10 +3142,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x210: /* movss xmm, ea */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
- gen_op_movl_T0_0();
+ tcg_gen_movi_tl(cpu_T[0], 0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)));
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
@@ -3418,9 +3157,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x310: /* movsd xmm, ea */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
- gen_op_movl_T0_0();
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
+ tcg_gen_movi_tl(cpu_T[0], 0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
} else {
@@ -3432,8 +3172,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x012: /* movlps */
case 0x112: /* movlpd */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
/* movhlps */
rm = (modrm & 7) | REX_B(s);
@@ -3443,8 +3184,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x212: /* movsldup */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
@@ -3459,8 +3200,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x312: /* movddup */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
@@ -3472,8 +3214,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x016: /* movhps */
case 0x116: /* movhpd */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(1)));
} else {
/* movlhps */
rm = (modrm & 7) | REX_B(s);
@@ -3483,8 +3226,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x216: /* movshdup */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)),
@@ -3520,36 +3263,37 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x7e: /* movd ea, mm */
#ifdef TARGET_X86_64
- if (s->dflag == 2) {
+ if (s->dflag == MO_64) {
tcg_gen_ld_i64(cpu_T[0], cpu_env,
offsetof(CPUX86State,fpregs[reg].mmx));
- gen_ldst_modrm(env, s, modrm, OT_QUAD, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1);
} else
#endif
{
tcg_gen_ld32u_tl(cpu_T[0], cpu_env,
offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0)));
- gen_ldst_modrm(env, s, modrm, OT_LONG, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1);
}
break;
case 0x17e: /* movd ea, xmm */
#ifdef TARGET_X86_64
- if (s->dflag == 2) {
+ if (s->dflag == MO_64) {
tcg_gen_ld_i64(cpu_T[0], cpu_env,
offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
- gen_ldst_modrm(env, s, modrm, OT_QUAD, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1);
} else
#endif
{
tcg_gen_ld32u_tl(cpu_T[0], cpu_env,
offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
- gen_ldst_modrm(env, s, modrm, OT_LONG, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1);
}
break;
case 0x27e: /* movq xmm, ea */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
@@ -3559,8 +3303,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x7f: /* movq ea, mm */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx));
} else {
rm = (modrm & 7);
gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx),
@@ -3574,8 +3318,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x17f: /* movdqa ea, xmm */
case 0x27f: /* movdqu ea, xmm */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ gen_lea_modrm(env, s, modrm);
+ gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]),
@@ -3584,9 +3328,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x211: /* movss ea, xmm */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0);
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)),
@@ -3595,8 +3339,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x311: /* movsd ea, xmm */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
@@ -3606,8 +3351,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x013: /* movlps */
case 0x113: /* movlpd */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
goto illegal_op;
}
@@ -3615,8 +3361,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x017: /* movhps */
case 0x117: /* movhpd */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(1)));
} else {
goto illegal_op;
}
@@ -3632,15 +3379,15 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
}
val = cpu_ldub_code(env, s->pc++);
if (is_xmm) {
- gen_op_movl_T0_im(val);
+ tcg_gen_movi_tl(cpu_T[0], val);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
- gen_op_movl_T0_0();
+ tcg_gen_movi_tl(cpu_T[0], 0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(1)));
op1_offset = offsetof(CPUX86State,xmm_t0);
} else {
- gen_op_movl_T0_im(val);
+ tcg_gen_movi_tl(cpu_T[0], val);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(0)));
- gen_op_movl_T0_0();
+ tcg_gen_movi_tl(cpu_T[0], 0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(1)));
op1_offset = offsetof(CPUX86State,mmx_t0);
}
@@ -3665,24 +3412,22 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
offsetof(CPUX86State,xmm_regs[rm]));
gen_helper_movmskps(cpu_tmp2_i32, cpu_env, cpu_ptr0);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_mov_reg_T0(OT_LONG, reg);
+ tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32);
break;
case 0x150: /* movmskpd */
rm = (modrm & 7) | REX_B(s);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
offsetof(CPUX86State,xmm_regs[rm]));
gen_helper_movmskpd(cpu_tmp2_i32, cpu_env, cpu_ptr0);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_mov_reg_T0(OT_LONG, reg);
+ tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32);
break;
case 0x02a: /* cvtpi2ps */
case 0x12a: /* cvtpi2pd */
gen_helper_enter_mmx(cpu_env);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State,mmx_t0);
- gen_ldq_env_A0(s->mem_index, op2_offset);
+ gen_ldq_env_A0(s, op2_offset);
} else {
rm = (modrm & 7);
op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
@@ -3702,11 +3447,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
break;
case 0x22a: /* cvtsi2ss */
case 0x32a: /* cvtsi2sd */
- ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
- if (ot == OT_LONG) {
+ if (ot == MO_32) {
SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1];
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
sse_fn_epi(cpu_env, cpu_ptr0, cpu_tmp2_i32);
@@ -3725,9 +3470,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x12d: /* cvtpd2pi */
gen_helper_enter_mmx(cpu_env);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State,xmm_t0);
- gen_ldo_env_A0(s->mem_index, op2_offset);
+ gen_ldo_env_A0(s, op2_offset);
} else {
rm = (modrm & 7) | REX_B(s);
op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
@@ -3754,13 +3499,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x32c: /* cvttsd2si */
case 0x22d: /* cvtss2si */
case 0x32d: /* cvtsd2si */
- ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
if ((b >> 8) & 1) {
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_Q(0)));
+ gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_Q(0)));
} else {
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
}
op2_offset = offsetof(CPUX86State,xmm_t0);
@@ -3769,7 +3514,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
}
tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset);
- if (ot == OT_LONG) {
+ if (ot == MO_32) {
SSEFunc_i_ep sse_fn_i_ep =
sse_op_table3bi[((b >> 7) & 2) | (b & 1)];
sse_fn_i_ep(cpu_tmp2_i32, cpu_env, cpu_ptr0);
@@ -3783,12 +3528,12 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
goto illegal_op;
#endif
}
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
case 0xc4: /* pinsrw */
case 0x1c4:
s->rip_offset = 1;
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
val = cpu_ldub_code(env, s->pc++);
if (b1) {
val &= 7;
@@ -3804,7 +3549,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x1c5:
if (mod != 3)
goto illegal_op;
- ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
val = cpu_ldub_code(env, s->pc++);
if (b1) {
val &= 7;
@@ -3818,12 +3563,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val)));
}
reg = ((modrm >> 3) & 7) | rex_r;
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
case 0x1d6: /* movq ea, xmm */
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_lea_modrm(env, s, modrm);
+ gen_stq_env_A0(s, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(0)));
} else {
rm = (modrm & 7) | REX_B(s);
gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
@@ -3857,9 +3603,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx));
gen_helper_pmovmskb_mmx(cpu_tmp2_i32, cpu_env, cpu_ptr0);
}
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
reg = ((modrm >> 3) & 7) | rex_r;
- gen_op_mov_reg_T0(OT_LONG, reg);
+ tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32);
break;
case 0x138:
@@ -3889,33 +3634,32 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]);
} else {
op2_offset = offsetof(CPUX86State,xmm_t0);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
switch (b) {
case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */
case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */
case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */
- gen_ldq_env_A0(s->mem_index, op2_offset +
+ gen_ldq_env_A0(s, op2_offset +
offsetof(XMMReg, XMM_Q(0)));
break;
case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */
case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */
- tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
- (s->mem_index >> 2) - 1);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset +
offsetof(XMMReg, XMM_L(0)));
break;
case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */
- tcg_gen_qemu_ld16u(cpu_tmp0, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_ld_tl(cpu_tmp0, cpu_A0,
+ s->mem_index, MO_LEUW);
tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset +
offsetof(XMMReg, XMM_W(0)));
break;
case 0x2a: /* movntqda */
- gen_ldo_env_A0(s->mem_index, op1_offset);
+ gen_ldo_env_A0(s, op1_offset);
return;
default:
- gen_ldo_env_A0(s->mem_index, op2_offset);
+ gen_ldo_env_A0(s, op2_offset);
}
}
} else {
@@ -3924,8 +3668,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
} else {
op2_offset = offsetof(CPUX86State,mmx_t0);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, op2_offset);
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, op2_offset);
}
}
if (sse_fn_epp == SSE_SPECIAL) {
@@ -3957,21 +3701,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
goto illegal_op;
}
if ((b & 0xff) == 0xf0) {
- ot = OT_BYTE;
- } else if (s->dflag != 2) {
- ot = (s->prefix & PREFIX_DATA ? OT_WORD : OT_LONG);
+ ot = MO_8;
+ } else if (s->dflag != MO_64) {
+ ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32);
} else {
- ot = OT_QUAD;
+ ot = MO_64;
}
- gen_op_mov_TN_reg(OT_LONG, 0, reg);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[reg]);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
gen_helper_crc32(cpu_T[0], cpu_tmp2_i32,
cpu_T[0], tcg_const_i32(8 << ot));
- ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
- gen_op_mov_reg_T0(ot, reg);
+ ot = mo_64_32(s->dflag);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
case 0x1f0: /* crc32 or movbe */
@@ -3988,50 +3731,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) {
goto illegal_op;
}
- if (s->dflag != 2) {
- ot = (s->prefix & PREFIX_DATA ? OT_WORD : OT_LONG);
+ if (s->dflag != MO_64) {
+ ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32);
} else {
- ot = OT_QUAD;
+ ot = MO_64;
}
- /* Load the data incoming to the bswap. Note that the TCG
- implementation of bswap requires the input be zero
- extended. In the case of the loads, we simply know that
- gen_op_ld_v via gen_ldst_modrm does that already. */
+ gen_lea_modrm(env, s, modrm);
if ((b & 1) == 0) {
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
+ tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0,
+ s->mem_index, ot | MO_BE);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
} else {
- switch (ot) {
- case OT_WORD:
- tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[reg]);
- break;
- default:
- tcg_gen_ext32u_tl(cpu_T[0], cpu_regs[reg]);
- break;
- case OT_QUAD:
- tcg_gen_mov_tl(cpu_T[0], cpu_regs[reg]);
- break;
- }
- }
-
- switch (ot) {
- case OT_WORD:
- tcg_gen_bswap16_tl(cpu_T[0], cpu_T[0]);
- break;
- default:
- tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]);
- break;
-#ifdef TARGET_X86_64
- case OT_QUAD:
- tcg_gen_bswap64_tl(cpu_T[0], cpu_T[0]);
- break;
-#endif
- }
-
- if ((b & 1) == 0) {
- gen_op_mov_reg_T0(ot, reg);
- } else {
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
+ tcg_gen_qemu_st_tl(cpu_regs[reg], cpu_A0,
+ s->mem_index, ot | MO_BE);
}
break;
@@ -4041,10 +3754,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
tcg_gen_andc_tl(cpu_T[0], cpu_regs[s->vex_v], cpu_T[0]);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
gen_op_update1_cc();
set_cc_op(s, CC_OP_LOGICB + ot);
break;
@@ -4055,7 +3768,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
{
TCGv bound, zero;
@@ -4065,7 +3778,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_ext8u_tl(cpu_A0, cpu_regs[s->vex_v]);
tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_A0);
- bound = tcg_const_tl(ot == OT_QUAD ? 63 : 31);
+ bound = tcg_const_tl(ot == MO_64 ? 63 : 31);
zero = tcg_const_tl(0);
tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T[0], cpu_A0, bound,
cpu_T[0], zero);
@@ -4083,7 +3796,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_subi_tl(cpu_T[1], cpu_T[1], 1);
tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
gen_op_update1_cc();
set_cc_op(s, CC_OP_LOGICB + ot);
}
@@ -4095,11 +3808,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
tcg_gen_ext8u_tl(cpu_T[1], cpu_regs[s->vex_v]);
{
- TCGv bound = tcg_const_tl(ot == OT_QUAD ? 63 : 31);
+ TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31);
/* Note that since we're using BMILG (in order to get O
cleared) we need to store the inverse into C. */
tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src,
@@ -4111,7 +3824,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_movi_tl(cpu_A0, -1);
tcg_gen_shl_tl(cpu_A0, cpu_A0, cpu_T[1]);
tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_A0);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
gen_op_update1_cc();
set_cc_op(s, CC_OP_BMILGB + ot);
break;
@@ -4122,7 +3835,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
switch (ot) {
default:
@@ -4134,7 +3847,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp3_i32);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
tcg_gen_mulu2_i64(cpu_regs[s->vex_v], cpu_regs[reg],
cpu_T[0], cpu_regs[R_EDX]);
break;
@@ -4148,11 +3861,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
/* Note that by zero-extending the mask operand, we
automatically handle zero-extending the result. */
- if (s->dflag == 2) {
+ if (ot == MO_64) {
tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]);
} else {
tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]);
@@ -4166,11 +3879,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
/* Note that by zero-extending the mask operand, we
automatically handle zero-extending the result. */
- if (s->dflag == 2) {
+ if (ot == MO_64) {
tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]);
} else {
tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]);
@@ -4186,7 +3899,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
TCGv carry_in, carry_out, zero;
int end_op;
- ot = (s->dflag == 2 ? OT_QUAD : OT_LONG);
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
/* Re-use the carry-out from a previous round. */
@@ -4230,7 +3943,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
switch (ot) {
#ifdef TARGET_X86_64
- case OT_LONG:
+ case MO_32:
/* If we know TL is 64-bit, and we want a 32-bit
result, just do everything in 64-bit arithmetic. */
tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]);
@@ -4265,9 +3978,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = (s->dflag == 2 ? OT_QUAD : OT_LONG);
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- if (ot == OT_QUAD) {
+ if (ot == MO_64) {
tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 63);
} else {
tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 31);
@@ -4275,17 +3988,17 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
if (b == 0x1f7) {
tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
} else if (b == 0x2f7) {
- if (ot != OT_QUAD) {
+ if (ot != MO_64) {
tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
}
tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
} else {
- if (ot != OT_QUAD) {
+ if (ot != MO_64) {
tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]);
}
tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
}
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
case 0x0f3:
@@ -4297,14 +4010,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
switch (reg & 7) {
case 1: /* blsr By,Ey */
tcg_gen_neg_tl(cpu_T[1], cpu_T[0]);
tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(ot, s->vex_v);
+ gen_op_mov_reg_v(ot, s->vex_v, cpu_T[0]);
gen_op_update2_cc();
set_cc_op(s, CC_OP_BMILGB + ot);
break;
@@ -4354,52 +4067,55 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
goto illegal_op;
if (sse_fn_eppi == SSE_SPECIAL) {
- ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
rm = (modrm & 7) | REX_B(s);
if (mod != 3)
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
reg = ((modrm >> 3) & 7) | rex_r;
val = cpu_ldub_code(env, s->pc++);
switch (b) {
case 0x14: /* pextrb */
tcg_gen_ld8u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
xmm_regs[reg].XMM_B(val & 15)));
- if (mod == 3)
- gen_op_mov_reg_T0(ot, rm);
- else
- tcg_gen_qemu_st8(cpu_T[0], cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ } else {
+ tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0,
+ s->mem_index, MO_UB);
+ }
break;
case 0x15: /* pextrw */
tcg_gen_ld16u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
xmm_regs[reg].XMM_W(val & 7)));
- if (mod == 3)
- gen_op_mov_reg_T0(ot, rm);
- else
- tcg_gen_qemu_st16(cpu_T[0], cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ } else {
+ tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0,
+ s->mem_index, MO_LEUW);
+ }
break;
case 0x16:
- if (ot == OT_LONG) { /* pextrd */
+ if (ot == MO_32) { /* pextrd */
tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env,
offsetof(CPUX86State,
xmm_regs[reg].XMM_L(val & 3)));
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- if (mod == 3)
- gen_op_mov_reg_v(ot, rm, cpu_T[0]);
- else
- tcg_gen_qemu_st32(cpu_T[0], cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ tcg_gen_extu_i32_tl(cpu_regs[rm], cpu_tmp2_i32);
+ } else {
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
+ }
} else { /* pextrq */
#ifdef TARGET_X86_64
tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env,
offsetof(CPUX86State,
xmm_regs[reg].XMM_Q(val & 1)));
- if (mod == 3)
- gen_op_mov_reg_v(ot, rm, cpu_tmp1_i64);
- else
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ tcg_gen_mov_i64(cpu_regs[rm], cpu_tmp1_i64);
+ } else {
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
+ }
#else
goto illegal_op;
#endif
@@ -4408,18 +4124,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x17: /* extractps */
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
xmm_regs[reg].XMM_L(val & 3)));
- if (mod == 3)
- gen_op_mov_reg_T0(ot, rm);
- else
- tcg_gen_qemu_st32(cpu_T[0], cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ } else {
+ tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0,
+ s->mem_index, MO_LEUL);
+ }
break;
case 0x20: /* pinsrb */
- if (mod == 3)
- gen_op_mov_TN_reg(OT_LONG, 0, rm);
- else
- tcg_gen_qemu_ld8u(cpu_T[0], cpu_A0,
- (s->mem_index >> 2) - 1);
+ if (mod == 3) {
+ gen_op_mov_v_reg(MO_32, cpu_T[0], rm);
+ } else {
+ tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0,
+ s->mem_index, MO_UB);
+ }
tcg_gen_st8_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
xmm_regs[reg].XMM_B(val & 15)));
break;
@@ -4429,9 +4147,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
offsetof(CPUX86State,xmm_regs[rm]
.XMM_L((val >> 6) & 3)));
} else {
- tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
- (s->mem_index >> 2) - 1);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
}
tcg_gen_st_i32(cpu_tmp2_i32, cpu_env,
offsetof(CPUX86State,xmm_regs[reg]
@@ -4454,23 +4171,24 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
xmm_regs[reg].XMM_L(3)));
break;
case 0x22:
- if (ot == OT_LONG) { /* pinsrd */
- if (mod == 3)
- gen_op_mov_v_reg(ot, cpu_tmp0, rm);
- else
- tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
- (s->mem_index >> 2) - 1);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ if (ot == MO_32) { /* pinsrd */
+ if (mod == 3) {
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[rm]);
+ } else {
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
+ }
tcg_gen_st_i32(cpu_tmp2_i32, cpu_env,
offsetof(CPUX86State,
xmm_regs[reg].XMM_L(val & 3)));
} else { /* pinsrq */
#ifdef TARGET_X86_64
- if (mod == 3)
+ if (mod == 3) {
gen_op_mov_v_reg(ot, cpu_tmp1_i64, rm);
- else
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ } else {
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
+ }
tcg_gen_st_i64(cpu_tmp1_i64, cpu_env,
offsetof(CPUX86State,
xmm_regs[reg].XMM_Q(val & 1)));
@@ -4489,8 +4207,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]);
} else {
op2_offset = offsetof(CPUX86State,xmm_t0);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldo_env_A0(s->mem_index, op2_offset);
+ gen_lea_modrm(env, s, modrm);
+ gen_ldo_env_A0(s, op2_offset);
}
} else {
op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
@@ -4498,8 +4216,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
} else {
op2_offset = offsetof(CPUX86State,mmx_t0);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_ldq_env_A0(s->mem_index, op2_offset);
+ gen_lea_modrm(env, s, modrm);
+ gen_ldq_env_A0(s, op2_offset);
}
}
val = cpu_ldub_code(env, s->pc++);
@@ -4507,9 +4225,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
if ((b & 0xfc) == 0x60) { /* pcmpXstrX */
set_cc_op(s, CC_OP_EFLAGS);
- if (s->dflag == 2)
+ if (s->dflag == MO_64) {
/* The helper must use entire 64-bit gp registers */
val |= 1 << 8;
+ }
}
tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
@@ -4530,17 +4249,17 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
|| s->vex_l != 0) {
goto illegal_op;
}
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
b = cpu_ldub_code(env, s->pc++);
- if (ot == OT_QUAD) {
+ if (ot == MO_64) {
tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], b & 63);
} else {
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, b & 31);
tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
}
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
default:
@@ -4565,21 +4284,22 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
if (is_xmm) {
op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State,xmm_t0);
if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) ||
b == 0xc2)) {
/* specific case for SSE single instructions */
if (b1 == 2) {
/* 32 bit access */
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0);
tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
} else {
/* 64 bit access */
- gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0)));
+ gen_ldq_env_A0(s, offsetof(CPUX86State,
+ xmm_t0.XMM_D(0)));
}
} else {
- gen_ldo_env_A0(s->mem_index, op2_offset);
+ gen_ldo_env_A0(s, op2_offset);
}
} else {
rm = (modrm & 7) | REX_B(s);
@@ -4588,9 +4308,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
} else {
op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
op2_offset = offsetof(CPUX86State,mmx_t0);
- gen_ldq_env_A0(s->mem_index, op2_offset);
+ gen_ldq_env_A0(s, op2_offset);
} else {
rm = (modrm & 7);
op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
@@ -4633,16 +4353,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
/* maskmov : we must prepare A0 */
if (mod != 3)
goto illegal_op;
-#ifdef TARGET_X86_64
- if (s->aflag == 2) {
- gen_op_movq_A0_reg(R_EDI);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_EDI);
- if (s->aflag == 0)
- gen_op_andl_A0_ffff();
- }
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EDI]);
+ gen_extu(s->aflag, cpu_A0);
gen_add_A0_ds_seg(s);
tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
@@ -4668,9 +4380,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
target_ulong pc_start)
{
- int b, prefixes, aflag, dflag;
- int shift, ot;
- int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val;
+ int b, prefixes;
+ int shift;
+ TCGMemOp ot, aflag, dflag;
+ int modrm, reg, rm, mod, op, opreg, val;
target_ulong next_eip, tval;
int rex_w, rex_r;
@@ -4805,19 +4518,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/* In 64-bit mode, the default data size is 32-bit. Select 64-bit
data with rex_w, and 16-bit data with 0x66; rex_w takes precedence
over 0x66 if both are present. */
- dflag = (rex_w > 0 ? 2 : prefixes & PREFIX_DATA ? 0 : 1);
+ dflag = (rex_w > 0 ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32);
/* In 64-bit mode, 0x67 selects 32-bit addressing. */
- aflag = (prefixes & PREFIX_ADR ? 1 : 2);
+ aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64);
} else {
/* In 16/32-bit mode, 0x66 selects the opposite data size. */
- dflag = s->code32;
- if (prefixes & PREFIX_DATA) {
- dflag ^= 1;
+ if (s->code32 ^ ((prefixes & PREFIX_DATA) != 0)) {
+ dflag = MO_32;
+ } else {
+ dflag = MO_16;
}
/* In 16/32-bit mode, 0x67 selects the opposite addressing. */
- aflag = s->code32;
- if (prefixes & PREFIX_ADR) {
- aflag ^= 1;
+ if (s->code32 ^ ((prefixes & PREFIX_ADR) != 0)) {
+ aflag = MO_32;
+ } else {
+ aflag = MO_16;
}
}
@@ -4853,10 +4568,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
op = (b >> 3) & 7;
f = (b >> 1) & 3;
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
switch(f) {
case 0: /* OP Ev, Gv */
@@ -4865,19 +4577,19 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
opreg = OR_TMP0;
} else if (op == OP_XORL && rm == reg) {
xor_zero:
/* xor reg, reg optimisation */
set_cc_op(s, CC_OP_CLR);
- gen_op_movl_T0_0();
- gen_op_mov_reg_T0(ot, reg);
+ tcg_gen_movi_tl(cpu_T[0], 0);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
} else {
opreg = rm;
}
- gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], reg);
gen_op(s, op, ot, opreg);
break;
case 1: /* OP Gv, Ev */
@@ -4886,18 +4598,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
reg = ((modrm >> 3) & 7) | rex_r;
rm = (modrm & 7) | REX_B(s);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
} else if (op == OP_XORL && rm == reg) {
goto xor_zero;
} else {
- gen_op_mov_TN_reg(ot, 1, rm);
+ gen_op_mov_v_reg(ot, cpu_T[1], rm);
}
gen_op(s, op, ot, reg);
break;
case 2: /* OP A, Iv */
val = insn_get(env, s, ot);
- gen_op_movl_T1_im(val);
+ tcg_gen_movi_tl(cpu_T[1], val);
gen_op(s, op, ot, OR_EAX);
break;
}
@@ -4913,10 +4625,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
{
int val;
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
@@ -4928,7 +4637,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
s->rip_offset = 1;
else
s->rip_offset = insn_const_size(ot);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
opreg = OR_TMP0;
} else {
opreg = rm;
@@ -4942,10 +4651,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
val = insn_get(env, s, ot);
break;
case 0x83:
- val = (int8_t)insn_get(env, s, OT_BYTE);
+ val = (int8_t)insn_get(env, s, MO_8);
break;
}
- gen_op_movl_T1_im(val);
+ tcg_gen_movi_tl(cpu_T[1], val);
gen_op(s, op, ot, opreg);
}
break;
@@ -4953,19 +4662,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/**************************/
/* inc, dec, and other misc arith */
case 0x40 ... 0x47: /* inc Gv */
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
gen_inc(s, ot, OR_EAX + (b & 7), 1);
break;
case 0x48 ... 0x4f: /* dec Gv */
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
gen_inc(s, ot, OR_EAX + (b & 7), -1);
break;
case 0xf6: /* GRP3 */
case 0xf7:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
@@ -4974,65 +4680,65 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (mod != 3) {
if (op == 0)
s->rip_offset = insn_const_size(ot);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
}
switch(op) {
case 0: /* test */
val = insn_get(env, s, ot);
- gen_op_movl_T1_im(val);
+ tcg_gen_movi_tl(cpu_T[1], val);
gen_op_testl_T0_T1_cc();
set_cc_op(s, CC_OP_LOGICB + ot);
break;
case 2: /* not */
tcg_gen_not_tl(cpu_T[0], cpu_T[0]);
if (mod != 3) {
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
}
break;
case 3: /* neg */
tcg_gen_neg_tl(cpu_T[0], cpu_T[0]);
if (mod != 3) {
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
}
gen_op_update_neg_cc();
set_cc_op(s, CC_OP_SUBB + ot);
break;
case 4: /* mul */
switch(ot) {
- case OT_BYTE:
- gen_op_mov_TN_reg(OT_BYTE, 1, R_EAX);
+ case MO_8:
+ gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX);
tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]);
tcg_gen_ext8u_tl(cpu_T[1], cpu_T[1]);
/* XXX: use 32 bit mul which could be faster */
tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
tcg_gen_andi_tl(cpu_cc_src, cpu_T[0], 0xff00);
set_cc_op(s, CC_OP_MULB);
break;
- case OT_WORD:
- gen_op_mov_TN_reg(OT_WORD, 1, R_EAX);
+ case MO_16:
+ gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX);
tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
tcg_gen_ext16u_tl(cpu_T[1], cpu_T[1]);
/* XXX: use 32 bit mul which could be faster */
tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16);
- gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]);
tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]);
set_cc_op(s, CC_OP_MULW);
break;
default:
- case OT_LONG:
+ case MO_32:
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]);
tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32,
@@ -5044,7 +4750,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
set_cc_op(s, CC_OP_MULL);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX],
cpu_T[0], cpu_regs[R_EAX]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
@@ -5056,34 +4762,34 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 5: /* imul */
switch(ot) {
- case OT_BYTE:
- gen_op_mov_TN_reg(OT_BYTE, 1, R_EAX);
+ case MO_8:
+ gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX);
tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
tcg_gen_ext8s_tl(cpu_T[1], cpu_T[1]);
/* XXX: use 32 bit mul which could be faster */
tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
tcg_gen_ext8s_tl(cpu_tmp0, cpu_T[0]);
tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
set_cc_op(s, CC_OP_MULB);
break;
- case OT_WORD:
- gen_op_mov_TN_reg(OT_WORD, 1, R_EAX);
+ case MO_16:
+ gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX);
tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]);
/* XXX: use 32 bit mul which could be faster */
tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]);
tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16);
- gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]);
set_cc_op(s, CC_OP_MULW);
break;
default:
- case OT_LONG:
+ case MO_32:
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]);
tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32,
@@ -5097,7 +4803,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
set_cc_op(s, CC_OP_MULL);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX],
cpu_T[0], cpu_regs[R_EAX]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
@@ -5110,21 +4816,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 6: /* div */
switch(ot) {
- case OT_BYTE:
+ case MO_8:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_divb_AL(cpu_env, cpu_T[0]);
break;
- case OT_WORD:
+ case MO_16:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_divw_AX(cpu_env, cpu_T[0]);
break;
default:
- case OT_LONG:
+ case MO_32:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_divl_EAX(cpu_env, cpu_T[0]);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_divq_EAX(cpu_env, cpu_T[0]);
break;
@@ -5133,21 +4839,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 7: /* idiv */
switch(ot) {
- case OT_BYTE:
+ case MO_8:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_idivb_AL(cpu_env, cpu_T[0]);
break;
- case OT_WORD:
+ case MO_16:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_idivw_AX(cpu_env, cpu_T[0]);
break;
default:
- case OT_LONG:
+ case MO_32:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_idivl_EAX(cpu_env, cpu_T[0]);
break;
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
gen_jmp_im(pc_start - s->cs_base);
gen_helper_idivq_EAX(cpu_env, cpu_T[0]);
break;
@@ -5161,10 +4867,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xfe: /* GRP4 */
case 0xff: /* GRP5 */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
@@ -5176,20 +4879,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s)) {
if (op == 2 || op == 4) {
/* operand size for jumps is 64 bit */
- ot = OT_QUAD;
+ ot = MO_64;
} else if (op == 3 || op == 5) {
- ot = dflag ? OT_LONG + (rex_w == 1) : OT_WORD;
+ ot = dflag != MO_16 ? MO_32 + (rex_w == 1) : MO_16;
} else if (op == 6) {
/* default push size is 64 bit */
- ot = dflag ? OT_QUAD : OT_WORD;
+ ot = mo_pushpop(s, dflag);
}
}
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
if (op >= 2 && op != 3 && op != 5)
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
}
switch(op) {
@@ -5209,44 +4912,46 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 2: /* call Ev */
/* XXX: optimize if memory (no 'and' is necessary) */
- if (s->dflag == 0)
- gen_op_andl_T0_ffff();
+ if (dflag == MO_16) {
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
+ }
next_eip = s->pc - s->cs_base;
- gen_movtl_T1_im(next_eip);
- gen_push_T1(s);
- gen_op_jmp_T0();
+ tcg_gen_movi_tl(cpu_T[1], next_eip);
+ gen_push_v(s, cpu_T[1]);
+ gen_op_jmp_v(cpu_T[0]);
gen_eob(s);
break;
case 3: /* lcall Ev */
- gen_op_ld_T1_A0(ot + s->mem_index);
- gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
- gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
+ gen_add_A0_im(s, 1 << ot);
+ gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0);
do_lcall:
if (s->pe && !s->vm86) {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T[1],
- tcg_const_i32(dflag),
+ tcg_const_i32(dflag - 1),
tcg_const_i32(s->pc - pc_start));
} else {
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T[1],
- tcg_const_i32(dflag),
+ tcg_const_i32(dflag - 1),
tcg_const_i32(s->pc - s->cs_base));
}
gen_eob(s);
break;
case 4: /* jmp Ev */
- if (s->dflag == 0)
- gen_op_andl_T0_ffff();
- gen_op_jmp_T0();
+ if (dflag == MO_16) {
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
+ }
+ gen_op_jmp_v(cpu_T[0]);
gen_eob(s);
break;
case 5: /* ljmp Ev */
- gen_op_ld_T1_A0(ot + s->mem_index);
- gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
- gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
+ gen_add_A0_im(s, 1 << ot);
+ gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0);
do_ljmp:
if (s->pe && !s->vm86) {
gen_update_cc_op(s);
@@ -5256,13 +4961,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_const_i32(s->pc - pc_start));
} else {
gen_op_movl_seg_T0_vm(R_CS);
- gen_op_movl_T0_T1();
- gen_op_jmp_T0();
+ gen_op_jmp_v(cpu_T[1]);
}
gen_eob(s);
break;
case 6: /* push Ev */
- gen_push_T0(s);
+ gen_push_v(s, cpu_T[0]);
break;
default:
goto illegal_op;
@@ -5271,76 +4975,80 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x84: /* test Ev, Gv */
case 0x85:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], reg);
gen_op_testl_T0_T1_cc();
set_cc_op(s, CC_OP_LOGICB + ot);
break;
case 0xa8: /* test eAX, Iv */
case 0xa9:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
val = insn_get(env, s, ot);
- gen_op_mov_TN_reg(ot, 0, OR_EAX);
- gen_op_movl_T1_im(val);
+ gen_op_mov_v_reg(ot, cpu_T[0], OR_EAX);
+ tcg_gen_movi_tl(cpu_T[1], val);
gen_op_testl_T0_T1_cc();
set_cc_op(s, CC_OP_LOGICB + ot);
break;
case 0x98: /* CWDE/CBW */
+ switch (dflag) {
#ifdef TARGET_X86_64
- if (dflag == 2) {
- gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ case MO_64:
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX);
tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_QUAD, R_EAX);
- } else
+ gen_op_mov_reg_v(MO_64, R_EAX, cpu_T[0]);
+ break;
#endif
- if (dflag == 1) {
- gen_op_mov_TN_reg(OT_WORD, 0, R_EAX);
+ case MO_32:
+ gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX);
tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_LONG, R_EAX);
- } else {
- gen_op_mov_TN_reg(OT_BYTE, 0, R_EAX);
+ gen_op_mov_reg_v(MO_32, R_EAX, cpu_T[0]);
+ break;
+ case MO_16:
+ gen_op_mov_v_reg(MO_8, cpu_T[0], R_EAX);
tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
+ break;
+ default:
+ tcg_abort();
}
break;
case 0x99: /* CDQ/CWD */
+ switch (dflag) {
#ifdef TARGET_X86_64
- if (dflag == 2) {
- gen_op_mov_TN_reg(OT_QUAD, 0, R_EAX);
+ case MO_64:
+ gen_op_mov_v_reg(MO_64, cpu_T[0], R_EAX);
tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 63);
- gen_op_mov_reg_T0(OT_QUAD, R_EDX);
- } else
+ gen_op_mov_reg_v(MO_64, R_EDX, cpu_T[0]);
+ break;
#endif
- if (dflag == 1) {
- gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ case MO_32:
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX);
tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 31);
- gen_op_mov_reg_T0(OT_LONG, R_EDX);
- } else {
- gen_op_mov_TN_reg(OT_WORD, 0, R_EAX);
+ gen_op_mov_reg_v(MO_32, R_EDX, cpu_T[0]);
+ break;
+ case MO_16:
+ gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX);
tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 15);
- gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]);
+ break;
+ default:
+ tcg_abort();
}
break;
case 0x1af: /* imul Gv, Ev */
case 0x69: /* imul Gv, Ev, I */
case 0x6b:
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
if (b == 0x69)
@@ -5350,23 +5058,23 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
if (b == 0x69) {
val = insn_get(env, s, ot);
- gen_op_movl_T1_im(val);
+ tcg_gen_movi_tl(cpu_T[1], val);
} else if (b == 0x6b) {
- val = (int8_t)insn_get(env, s, OT_BYTE);
- gen_op_movl_T1_im(val);
+ val = (int8_t)insn_get(env, s, MO_8);
+ tcg_gen_movi_tl(cpu_T[1], val);
} else {
- gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], reg);
}
switch (ot) {
#ifdef TARGET_X86_64
- case OT_QUAD:
+ case MO_64:
tcg_gen_muls2_i64(cpu_regs[reg], cpu_T[1], cpu_T[0], cpu_T[1]);
tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]);
tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63);
tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T[1]);
break;
#endif
- case OT_LONG:
+ case MO_32:
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32,
@@ -5385,34 +5093,31 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]);
tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
}
set_cc_op(s, CC_OP_MULB + ot);
break;
case 0x1c0:
case 0x1c1: /* xadd Ev, Gv */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
if (mod == 3) {
rm = (modrm & 7) | REX_B(s);
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_op_mov_TN_reg(ot, 1, rm);
- gen_op_addl_T0_T1();
- gen_op_mov_reg_T1(ot, reg);
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], rm);
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
} else {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_op_ld_T1_A0(ot + s->mem_index);
- gen_op_addl_T0_T1();
- gen_op_st_T0_A0(ot + s->mem_index);
- gen_op_mov_reg_T1(ot, reg);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
}
gen_op_update2_cc();
set_cc_op(s, CC_OP_ADDB + ot);
@@ -5423,10 +5128,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
int label1, label2;
TCGv t0, t1, t2, a0;
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
@@ -5439,9 +5141,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
rm = (modrm & 7) | REX_B(s);
gen_op_mov_v_reg(ot, t0, rm);
} else {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
tcg_gen_mov_tl(a0, cpu_A0);
- gen_op_ld_v(ot + s->mem_index, t0, a0);
+ gen_op_ld_v(s, ot, t0, a0);
rm = 0; /* avoid warning */
}
label1 = gen_new_label();
@@ -5459,11 +5161,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/* perform no-op store cycle like physical cpu; must be
before changing accumulator to ensure idempotency if
the store faults and the instruction is restarted */
- gen_op_st_v(ot + s->mem_index, t0, a0);
+ gen_op_st_v(s, ot, t0, a0);
gen_op_mov_reg_v(ot, R_EAX, t0);
tcg_gen_br(label2);
gen_set_label(label1);
- gen_op_st_v(ot + s->mem_index, t1, a0);
+ gen_op_st_v(s, ot, t1, a0);
}
gen_set_label(label2);
tcg_gen_mov_tl(cpu_cc_src, t0);
@@ -5482,12 +5184,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if ((mod == 3) || ((modrm & 0x38) != 0x8))
goto illegal_op;
#ifdef TARGET_X86_64
- if (dflag == 2) {
+ if (dflag == MO_64) {
if (!(s->cpuid_ext_features & CPUID_EXT_CX16))
goto illegal_op;
gen_jmp_im(pc_start - s->cs_base);
gen_update_cc_op(s);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
gen_helper_cmpxchg16b(cpu_env, cpu_A0);
} else
#endif
@@ -5496,7 +5198,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
goto illegal_op;
gen_jmp_im(pc_start - s->cs_base);
gen_update_cc_op(s);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
gen_helper_cmpxchg8b(cpu_env, cpu_A0);
}
set_cc_op(s, CC_OP_EFLAGS);
@@ -5505,19 +5207,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/**************************/
/* push/pop */
case 0x50 ... 0x57: /* push */
- gen_op_mov_TN_reg(OT_LONG, 0, (b & 7) | REX_B(s));
- gen_push_T0(s);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], (b & 7) | REX_B(s));
+ gen_push_v(s, cpu_T[0]);
break;
case 0x58 ... 0x5f: /* pop */
- if (CODE64(s)) {
- ot = dflag ? OT_QUAD : OT_WORD;
- } else {
- ot = dflag + OT_WORD;
- }
- gen_pop_T0(s);
+ ot = gen_pop_T0(s);
/* NOTE: order is important for pop %sp */
- gen_pop_update(s);
- gen_op_mov_reg_T0(ot, (b & 7) | REX_B(s));
+ gen_pop_update(s, ot);
+ gen_op_mov_reg_v(ot, (b & 7) | REX_B(s), cpu_T[0]);
break;
case 0x60: /* pusha */
if (CODE64(s))
@@ -5531,38 +5228,29 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0x68: /* push Iv */
case 0x6a:
- if (CODE64(s)) {
- ot = dflag ? OT_QUAD : OT_WORD;
- } else {
- ot = dflag + OT_WORD;
- }
+ ot = mo_pushpop(s, dflag);
if (b == 0x68)
val = insn_get(env, s, ot);
else
- val = (int8_t)insn_get(env, s, OT_BYTE);
- gen_op_movl_T0_im(val);
- gen_push_T0(s);
+ val = (int8_t)insn_get(env, s, MO_8);
+ tcg_gen_movi_tl(cpu_T[0], val);
+ gen_push_v(s, cpu_T[0]);
break;
case 0x8f: /* pop Ev */
- if (CODE64(s)) {
- ot = dflag ? OT_QUAD : OT_WORD;
- } else {
- ot = dflag + OT_WORD;
- }
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
- gen_pop_T0(s);
+ ot = gen_pop_T0(s);
if (mod == 3) {
/* NOTE: order is important for pop %sp */
- gen_pop_update(s);
+ gen_pop_update(s, ot);
rm = (modrm & 7) | REX_B(s);
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
} else {
/* NOTE: order is important too for MMU exceptions */
s->popl_esp_hack = 1 << ot;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
s->popl_esp_hack = 0;
- gen_pop_update(s);
+ gen_pop_update(s, ot);
}
break;
case 0xc8: /* enter */
@@ -5577,23 +5265,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xc9: /* leave */
/* XXX: exception not precise (ESP is updated before potential exception) */
if (CODE64(s)) {
- gen_op_mov_TN_reg(OT_QUAD, 0, R_EBP);
- gen_op_mov_reg_T0(OT_QUAD, R_ESP);
+ gen_op_mov_v_reg(MO_64, cpu_T[0], R_EBP);
+ gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[0]);
} else if (s->ss32) {
- gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
- gen_op_mov_reg_T0(OT_LONG, R_ESP);
- } else {
- gen_op_mov_TN_reg(OT_WORD, 0, R_EBP);
- gen_op_mov_reg_T0(OT_WORD, R_ESP);
- }
- gen_pop_T0(s);
- if (CODE64(s)) {
- ot = dflag ? OT_QUAD : OT_WORD;
+ gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP);
+ gen_op_mov_reg_v(MO_32, R_ESP, cpu_T[0]);
} else {
- ot = dflag + OT_WORD;
+ gen_op_mov_v_reg(MO_16, cpu_T[0], R_EBP);
+ gen_op_mov_reg_v(MO_16, R_ESP, cpu_T[0]);
}
- gen_op_mov_reg_T0(ot, R_EBP);
- gen_pop_update(s);
+ ot = gen_pop_T0(s);
+ gen_op_mov_reg_v(ot, R_EBP, cpu_T[0]);
+ gen_pop_update(s, ot);
break;
case 0x06: /* push es */
case 0x0e: /* push cs */
@@ -5602,12 +5285,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s))
goto illegal_op;
gen_op_movl_T0_seg(b >> 3);
- gen_push_T0(s);
+ gen_push_v(s, cpu_T[0]);
break;
case 0x1a0: /* push fs */
case 0x1a8: /* push gs */
gen_op_movl_T0_seg((b >> 3) & 7);
- gen_push_T0(s);
+ gen_push_v(s, cpu_T[0]);
break;
case 0x07: /* pop es */
case 0x17: /* pop ss */
@@ -5615,9 +5298,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s))
goto illegal_op;
reg = b >> 3;
- gen_pop_T0(s);
+ ot = gen_pop_T0(s);
gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
- gen_pop_update(s);
+ gen_pop_update(s, ot);
if (reg == R_SS) {
/* if reg == SS, inhibit interrupts/trace. */
/* If several instructions disable interrupts, only the
@@ -5633,9 +5316,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0x1a1: /* pop fs */
case 0x1a9: /* pop gs */
- gen_pop_T0(s);
+ ot = gen_pop_T0(s);
gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base);
- gen_pop_update(s);
+ gen_pop_update(s, ot);
if (s->is_jmp) {
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
@@ -5646,10 +5329,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/* mov */
case 0x88:
case 0x89: /* mov Gv, Ev */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
@@ -5658,41 +5338,36 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xc6:
case 0xc7: /* mov Ev, Iv */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
if (mod != 3) {
s->rip_offset = insn_const_size(ot);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
}
val = insn_get(env, s, ot);
- gen_op_movl_T0_im(val);
- if (mod != 3)
- gen_op_st_T0_A0(ot + s->mem_index);
- else
- gen_op_mov_reg_T0(ot, (modrm & 7) | REX_B(s));
+ tcg_gen_movi_tl(cpu_T[0], val);
+ if (mod != 3) {
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
+ } else {
+ gen_op_mov_reg_v(ot, (modrm & 7) | REX_B(s), cpu_T[0]);
+ }
break;
case 0x8a:
case 0x8b: /* mov Ev, Gv */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = OT_WORD + dflag;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
case 0x8e: /* mov seg, Gv */
modrm = cpu_ldub_code(env, s->pc++);
reg = (modrm >> 3) & 7;
if (reg >= 6 || reg == R_CS)
goto illegal_op;
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
if (reg == R_SS) {
/* if reg == SS, inhibit interrupts/trace */
@@ -5714,10 +5389,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (reg >= 6)
goto illegal_op;
gen_op_movl_T0_seg(reg);
- if (mod == 3)
- ot = OT_WORD + dflag;
- else
- ot = OT_WORD;
+ ot = mod == 3 ? dflag : MO_16;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
@@ -5726,48 +5398,49 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x1be: /* movsbS Gv, Eb */
case 0x1bf: /* movswS Gv, Eb */
{
- int d_ot;
+ TCGMemOp d_ot;
+ TCGMemOp s_ot;
+
/* d_ot is the size of destination */
- d_ot = dflag + OT_WORD;
+ d_ot = dflag;
/* ot is the size of source */
- ot = (b & 1) + OT_BYTE;
+ ot = (b & 1) + MO_8;
+ /* s_ot is the sign+size of source */
+ s_ot = b & 8 ? MO_SIGN | ot : ot;
+
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
if (mod == 3) {
- gen_op_mov_TN_reg(ot, 0, rm);
- switch(ot | (b & 8)) {
- case OT_BYTE:
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
+ switch (s_ot) {
+ case MO_UB:
tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]);
break;
- case OT_BYTE | 8:
+ case MO_SB:
tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
break;
- case OT_WORD:
+ case MO_UW:
tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
break;
default:
- case OT_WORD | 8:
+ case MO_SW:
tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
break;
}
- gen_op_mov_reg_T0(d_ot, reg);
+ gen_op_mov_reg_v(d_ot, reg, cpu_T[0]);
} else {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- if (b & 8) {
- gen_op_lds_T0_A0(ot + s->mem_index);
- } else {
- gen_op_ldu_T0_A0(ot + s->mem_index);
- }
- gen_op_mov_reg_T0(d_ot, reg);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, s_ot, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(d_ot, reg, cpu_T[0]);
}
}
break;
case 0x8d: /* lea */
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
if (mod == 3)
@@ -5777,9 +5450,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
s->override = -1;
val = s->addseg;
s->addseg = 0;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
s->addseg = val;
- gen_op_mov_reg_A0(ot - OT_WORD, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_A0);
break;
case 0xa0: /* mov EAX, Ov */
@@ -5789,117 +5462,94 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
{
target_ulong offset_addr;
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
+ switch (s->aflag) {
#ifdef TARGET_X86_64
- if (s->aflag == 2) {
+ case MO_64:
offset_addr = cpu_ldq_code(env, s->pc);
s->pc += 8;
- gen_op_movq_A0_im(offset_addr);
- } else
+ break;
#endif
- {
- if (s->aflag) {
- offset_addr = insn_get(env, s, OT_LONG);
- } else {
- offset_addr = insn_get(env, s, OT_WORD);
- }
- gen_op_movl_A0_im(offset_addr);
+ default:
+ offset_addr = insn_get(env, s, s->aflag);
+ break;
}
+ tcg_gen_movi_tl(cpu_A0, offset_addr);
gen_add_A0_ds_seg(s);
if ((b & 2) == 0) {
- gen_op_ld_T0_A0(ot + s->mem_index);
- gen_op_mov_reg_T0(ot, R_EAX);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]);
} else {
- gen_op_mov_TN_reg(ot, 0, R_EAX);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_v_reg(ot, cpu_T[0], R_EAX);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
}
}
break;
case 0xd7: /* xlat */
-#ifdef TARGET_X86_64
- if (s->aflag == 2) {
- gen_op_movq_A0_reg(R_EBX);
- gen_op_mov_TN_reg(OT_QUAD, 0, R_EAX);
- tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xff);
- tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_EBX);
- gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
- tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xff);
- tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]);
- if (s->aflag == 0)
- gen_op_andl_A0_ffff();
- else
- tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
- }
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EBX]);
+ tcg_gen_ext8u_tl(cpu_T[0], cpu_regs[R_EAX]);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]);
+ gen_extu(s->aflag, cpu_A0);
gen_add_A0_ds_seg(s);
- gen_op_ldu_T0_A0(OT_BYTE + s->mem_index);
- gen_op_mov_reg_T0(OT_BYTE, R_EAX);
+ gen_op_ld_v(s, MO_8, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]);
break;
case 0xb0 ... 0xb7: /* mov R, Ib */
- val = insn_get(env, s, OT_BYTE);
- gen_op_movl_T0_im(val);
- gen_op_mov_reg_T0(OT_BYTE, (b & 7) | REX_B(s));
+ val = insn_get(env, s, MO_8);
+ tcg_gen_movi_tl(cpu_T[0], val);
+ gen_op_mov_reg_v(MO_8, (b & 7) | REX_B(s), cpu_T[0]);
break;
case 0xb8 ... 0xbf: /* mov R, Iv */
#ifdef TARGET_X86_64
- if (dflag == 2) {
+ if (dflag == MO_64) {
uint64_t tmp;
/* 64 bit case */
tmp = cpu_ldq_code(env, s->pc);
s->pc += 8;
reg = (b & 7) | REX_B(s);
- gen_movtl_T0_im(tmp);
- gen_op_mov_reg_T0(OT_QUAD, reg);
+ tcg_gen_movi_tl(cpu_T[0], tmp);
+ gen_op_mov_reg_v(MO_64, reg, cpu_T[0]);
} else
#endif
{
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
val = insn_get(env, s, ot);
reg = (b & 7) | REX_B(s);
- gen_op_movl_T0_im(val);
- gen_op_mov_reg_T0(ot, reg);
+ tcg_gen_movi_tl(cpu_T[0], val);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
}
break;
case 0x91 ... 0x97: /* xchg R, EAX */
do_xchg_reg_eax:
- ot = dflag + OT_WORD;
+ ot = dflag;
reg = (b & 7) | REX_B(s);
rm = R_EAX;
goto do_xchg_reg;
case 0x86:
case 0x87: /* xchg Ev, Gv */
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
if (mod == 3) {
rm = (modrm & 7) | REX_B(s);
do_xchg_reg:
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_op_mov_TN_reg(ot, 1, rm);
- gen_op_mov_reg_T0(ot, rm);
- gen_op_mov_reg_T1(ot, reg);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
} else {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_mov_TN_reg(ot, 0, reg);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
/* for xchg, lock is implicit */
if (!(prefixes & PREFIX_LOCK))
gen_helper_lock();
- gen_op_ld_T1_A0(ot + s->mem_index);
- gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
if (!(prefixes & PREFIX_LOCK))
gen_helper_unlock();
- gen_op_mov_reg_T1(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
}
break;
case 0xc4: /* les Gv */
@@ -5919,20 +5569,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x1b5: /* lgs Gv */
op = R_GS;
do_lxx:
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag != MO_16 ? MO_32 : MO_16;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T1_A0(ot + s->mem_index);
- gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, ot, cpu_T[1], cpu_A0);
+ gen_add_A0_im(s, 1 << ot);
/* load the segment first to handle exceptions properly */
- gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0);
gen_movl_seg_T0(s, op, pc_start - s->cs_base);
/* then put the data */
- gen_op_mov_reg_T1(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
if (s->is_jmp) {
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
@@ -5947,11 +5597,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
shift = 2;
grp2:
{
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
-
+ ot = mo_b_d(b, dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
op = (modrm >> 3) & 7;
@@ -5960,7 +5606,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (shift == 2) {
s->rip_offset = 1;
}
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
opreg = OR_TMP0;
} else {
opreg = (modrm & 7) | REX_B(s);
@@ -6004,18 +5650,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
op = 1;
shift = 0;
do_shiftd:
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
reg = ((modrm >> 3) & 7) | rex_r;
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
opreg = OR_TMP0;
} else {
opreg = rm;
}
- gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op_mov_v_reg(ot, cpu_T[1], reg);
if (shift) {
TCGv imm = tcg_const_tl(cpu_ldub_code(env, s->pc++));
@@ -6041,7 +5687,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
op = ((b & 7) << 3) | ((modrm >> 3) & 7);
if (mod != 3) {
/* memory op */
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
switch(op) {
case 0x00 ... 0x07: /* fxxxs */
case 0x10 ... 0x17: /* fixxxl */
@@ -6053,24 +5699,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
switch(op >> 4) {
case 0:
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
gen_helper_flds_FT0(cpu_env, cpu_tmp2_i32);
break;
case 1:
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32);
break;
case 2:
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
gen_helper_fldl_FT0(cpu_env, cpu_tmp1_i64);
break;
case 3:
default:
- gen_op_lds_T0_A0(OT_WORD + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LESW);
gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32);
break;
}
@@ -6092,24 +5738,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0:
switch(op >> 4) {
case 0:
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
gen_helper_flds_ST0(cpu_env, cpu_tmp2_i32);
break;
case 1:
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32);
break;
case 2:
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
gen_helper_fldl_ST0(cpu_env, cpu_tmp1_i64);
break;
case 3:
default:
- gen_op_lds_T0_A0(OT_WORD + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LESW);
gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32);
break;
}
@@ -6119,19 +5765,19 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
switch(op >> 4) {
case 1:
gen_helper_fisttl_ST0(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
break;
case 2:
gen_helper_fisttll_ST0(cpu_tmp1_i64, cpu_env);
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
break;
case 3:
default:
gen_helper_fistt_ST0(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUW);
break;
}
gen_helper_fpop(cpu_env);
@@ -6140,24 +5786,24 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
switch(op >> 4) {
case 0:
gen_helper_fsts_ST0(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
break;
case 1:
gen_helper_fistl_ST0(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
break;
case 2:
gen_helper_fstl_ST0(cpu_tmp1_i64, cpu_env);
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0,
+ s->mem_index, MO_LEQ);
break;
case 3:
default:
gen_helper_fist_ST0(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUW);
break;
}
if ((op & 7) == 3)
@@ -6168,22 +5814,22 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x0c: /* fldenv mem */
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(s->dflag));
+ gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1));
break;
case 0x0d: /* fldcw mem */
- gen_op_ld_T0_A0(OT_WORD + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUW);
gen_helper_fldcw(cpu_env, cpu_tmp2_i32);
break;
case 0x0e: /* fnstenv mem */
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(s->dflag));
+ gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1));
break;
case 0x0f: /* fnstcw mem */
gen_helper_fnstcw(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUW);
break;
case 0x1d: /* fldt mem */
gen_update_cc_op(s);
@@ -6199,17 +5845,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x2c: /* frstor mem */
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(s->dflag));
+ gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(dflag - 1));
break;
case 0x2e: /* fnsave mem */
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(s->dflag));
+ gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(dflag - 1));
break;
case 0x2f: /* fnstsw mem */
gen_helper_fnstsw(cpu_tmp2_i32, cpu_env);
- tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUW);
break;
case 0x3c: /* fbld */
gen_update_cc_op(s);
@@ -6223,14 +5869,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_helper_fpop(cpu_env);
break;
case 0x3d: /* fildll */
- tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ);
gen_helper_fildll_ST0(cpu_env, cpu_tmp1_i64);
break;
case 0x3f: /* fistpll */
gen_helper_fistll_ST0(cpu_tmp1_i64, cpu_env);
- tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
- (s->mem_index >> 2) - 1);
+ tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ);
gen_helper_fpop(cpu_env);
break;
default:
@@ -6496,7 +6140,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0:
gen_helper_fnstsw(cpu_tmp2_i32, cpu_env);
tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
- gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]);
break;
default:
goto illegal_op;
@@ -6553,11 +6197,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xa4: /* movsS */
case 0xa5:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
-
+ ot = mo_b_d(b, dflag);
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
} else {
@@ -6567,11 +6207,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xaa: /* stosS */
case 0xab:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
-
+ ot = mo_b_d(b, dflag);
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
} else {
@@ -6580,10 +6216,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xac: /* lodsS */
case 0xad:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
} else {
@@ -6592,10 +6225,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xae: /* scasS */
case 0xaf:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
if (prefixes & PREFIX_REPNZ) {
gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
} else if (prefixes & PREFIX_REPZ) {
@@ -6607,10 +6237,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xa6: /* cmpsS */
case 0xa7:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag + OT_WORD;
+ ot = mo_b_d(b, dflag);
if (prefixes & PREFIX_REPNZ) {
gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
} else if (prefixes & PREFIX_REPZ) {
@@ -6621,12 +6248,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0x6c: /* insS */
case 0x6d:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
- gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
- gen_op_andl_T0_ffff();
+ ot = mo_b_d32(b, dflag);
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4);
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
@@ -6640,12 +6263,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0x6e: /* outsS */
case 0x6f:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
- gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
- gen_op_andl_T0_ffff();
+ ot = mo_b_d32(b, dflag);
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
svm_is_rep(prefixes) | 4);
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
@@ -6663,19 +6282,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xe4:
case 0xe5:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = mo_b_d32(b, dflag);
val = cpu_ldub_code(env, s->pc++);
- gen_op_movl_T0_im(val);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
if (use_icount)
gen_io_start();
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_movi_i32(cpu_tmp2_i32, val);
gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32);
- gen_op_mov_reg_T1(ot, R_EAX);
+ gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]);
if (use_icount) {
gen_io_end();
gen_jmp(s, s->pc - s->cs_base);
@@ -6683,19 +6298,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xe6:
case 0xe7:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = mo_b_d32(b, dflag);
val = cpu_ldub_code(env, s->pc++);
- gen_op_movl_T0_im(val);
gen_check_io(s, ot, pc_start - s->cs_base,
svm_is_rep(prefixes));
- gen_op_mov_TN_reg(ot, 1, R_EAX);
+ gen_op_mov_v_reg(ot, cpu_T[1], R_EAX);
if (use_icount)
gen_io_start();
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_movi_i32(cpu_tmp2_i32, val);
tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32);
if (use_icount) {
@@ -6705,19 +6316,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xec:
case 0xed:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
- gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
- gen_op_andl_T0_ffff();
+ ot = mo_b_d32(b, dflag);
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
if (use_icount)
gen_io_start();
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32);
- gen_op_mov_reg_T1(ot, R_EAX);
+ gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]);
if (use_icount) {
gen_io_end();
gen_jmp(s, s->pc - s->cs_base);
@@ -6725,15 +6332,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xee:
case 0xef:
- if ((b & 1) == 0)
- ot = OT_BYTE;
- else
- ot = dflag ? OT_LONG : OT_WORD;
- gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
- gen_op_andl_T0_ffff();
+ ot = mo_b_d32(b, dflag);
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
svm_is_rep(prefixes));
- gen_op_mov_TN_reg(ot, 1, R_EAX);
+ gen_op_mov_v_reg(ot, cpu_T[1], R_EAX);
if (use_icount)
gen_io_start();
@@ -6751,21 +6354,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0xc2: /* ret im */
val = cpu_ldsw_code(env, s->pc);
s->pc += 2;
- gen_pop_T0(s);
- if (CODE64(s) && s->dflag)
- s->dflag = 2;
- gen_stack_update(s, val + (2 << s->dflag));
- if (s->dflag == 0)
- gen_op_andl_T0_ffff();
- gen_op_jmp_T0();
+ ot = gen_pop_T0(s);
+ gen_stack_update(s, val + (1 << ot));
+ /* Note that gen_pop_T0 uses a zero-extending load. */
+ gen_op_jmp_v(cpu_T[0]);
gen_eob(s);
break;
case 0xc3: /* ret */
- gen_pop_T0(s);
- gen_pop_update(s);
- if (s->dflag == 0)
- gen_op_andl_T0_ffff();
- gen_op_jmp_T0();
+ ot = gen_pop_T0(s);
+ gen_pop_update(s, ot);
+ /* Note that gen_pop_T0 uses a zero-extending load. */
+ gen_op_jmp_v(cpu_T[0]);
gen_eob(s);
break;
case 0xca: /* lret im */
@@ -6775,23 +6374,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (s->pe && !s->vm86) {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_lret_protected(cpu_env, tcg_const_i32(s->dflag),
+ gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1),
tcg_const_i32(val));
} else {
gen_stack_A0(s);
/* pop offset */
- gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
- if (s->dflag == 0)
- gen_op_andl_T0_ffff();
+ gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0);
/* NOTE: keeping EIP updated is not a problem in case of
exception */
- gen_op_jmp_T0();
+ gen_op_jmp_v(cpu_T[0]);
/* pop selector */
- gen_op_addl_A0_im(2 << s->dflag);
- gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
+ gen_op_addl_A0_im(1 << dflag);
+ gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0);
gen_op_movl_seg_T0_vm(R_CS);
/* add stack offset */
- gen_stack_update(s, val + (4 << s->dflag));
+ gen_stack_update(s, val + (2 << dflag));
}
gen_eob(s);
break;
@@ -6802,19 +6399,19 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_svm_check_intercept(s, pc_start, SVM_EXIT_IRET);
if (!s->pe) {
/* real mode */
- gen_helper_iret_real(cpu_env, tcg_const_i32(s->dflag));
+ gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1));
set_cc_op(s, CC_OP_EFLAGS);
} else if (s->vm86) {
if (s->iopl != 3) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- gen_helper_iret_real(cpu_env, tcg_const_i32(s->dflag));
+ gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1));
set_cc_op(s, CC_OP_EFLAGS);
}
} else {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_iret_protected(cpu_env, tcg_const_i32(s->dflag),
+ gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1),
tcg_const_i32(s->pc - s->cs_base));
set_cc_op(s, CC_OP_EFLAGS);
}
@@ -6822,18 +6419,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break;
case 0xe8: /* call im */
{
- if (dflag)
- tval = (int32_t)insn_get(env, s, OT_LONG);
- else
- tval = (int16_t)insn_get(env, s, OT_WORD);
+ if (dflag != MO_16) {
+ tval = (int32_t)insn_get(env, s, MO_32);
+ } else {
+ tval = (int16_t)insn_get(env, s, MO_16);
+ }
next_eip = s->pc - s->cs_base;
tval += next_eip;
- if (s->dflag == 0)
+ if (dflag == MO_16) {
tval &= 0xffff;
- else if(!CODE64(s))
+ } else if (!CODE64(s)) {
tval &= 0xffffffff;
- gen_movtl_T0_im(next_eip);
- gen_push_T0(s);
+ }
+ tcg_gen_movi_tl(cpu_T[0], next_eip);
+ gen_push_v(s, cpu_T[0]);
gen_jmp(s, tval);
}
break;
@@ -6843,24 +6442,26 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s))
goto illegal_op;
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
offset = insn_get(env, s, ot);
- selector = insn_get(env, s, OT_WORD);
+ selector = insn_get(env, s, MO_16);
- gen_op_movl_T0_im(selector);
- gen_op_movl_T1_imu(offset);
+ tcg_gen_movi_tl(cpu_T[0], selector);
+ tcg_gen_movi_tl(cpu_T[1], offset);
}
goto do_lcall;
case 0xe9: /* jmp im */
- if (dflag)
- tval = (int32_t)insn_get(env, s, OT_LONG);
- else
- tval = (int16_t)insn_get(env, s, OT_WORD);
+ if (dflag != MO_16) {
+ tval = (int32_t)insn_get(env, s, MO_32);
+ } else {
+ tval = (int16_t)insn_get(env, s, MO_16);
+ }
tval += s->pc - s->cs_base;
- if (s->dflag == 0)
+ if (dflag == MO_16) {
tval &= 0xffff;
- else if(!CODE64(s))
+ } else if (!CODE64(s)) {
tval &= 0xffffffff;
+ }
gen_jmp(s, tval);
break;
case 0xea: /* ljmp im */
@@ -6869,48 +6470,50 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s))
goto illegal_op;
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
offset = insn_get(env, s, ot);
- selector = insn_get(env, s, OT_WORD);
+ selector = insn_get(env, s, MO_16);
- gen_op_movl_T0_im(selector);
- gen_op_movl_T1_imu(offset);
+ tcg_gen_movi_tl(cpu_T[0], selector);
+ tcg_gen_movi_tl(cpu_T[1], offset);
}
goto do_ljmp;
case 0xeb: /* jmp Jb */
- tval = (int8_t)insn_get(env, s, OT_BYTE);
+ tval = (int8_t)insn_get(env, s, MO_8);
tval += s->pc - s->cs_base;
- if (s->dflag == 0)
+ if (dflag == MO_16) {
tval &= 0xffff;
+ }
gen_jmp(s, tval);
break;
case 0x70 ... 0x7f: /* jcc Jb */
- tval = (int8_t)insn_get(env, s, OT_BYTE);
+ tval = (int8_t)insn_get(env, s, MO_8);
goto do_jcc;
case 0x180 ... 0x18f: /* jcc Jv */
- if (dflag) {
- tval = (int32_t)insn_get(env, s, OT_LONG);
+ if (dflag != MO_16) {
+ tval = (int32_t)insn_get(env, s, MO_32);
} else {
- tval = (int16_t)insn_get(env, s, OT_WORD);
+ tval = (int16_t)insn_get(env, s, MO_16);
}
do_jcc:
next_eip = s->pc - s->cs_base;
tval += next_eip;
- if (s->dflag == 0)
+ if (dflag == MO_16) {
tval &= 0xffff;
+ }
gen_jcc(s, b, tval, next_eip);
break;
case 0x190 ... 0x19f: /* setcc Gv */
modrm = cpu_ldub_code(env, s->pc++);
gen_setcc1(s, b, cpu_T[0]);
- gen_ldst_modrm(env, s, modrm, OT_BYTE, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1);
break;
case 0x140 ... 0x14f: /* cmov Gv, Ev */
if (!(s->cpuid_features & CPUID_CMOV)) {
goto illegal_op;
}
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
gen_cmovcc1(env, s, ot, b, modrm, reg);
@@ -6925,7 +6528,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
} else {
gen_update_cc_op(s);
gen_helper_read_eflags(cpu_T[0], cpu_env);
- gen_push_T0(s);
+ gen_push_v(s, cpu_T[0]);
}
break;
case 0x9d: /* popf */
@@ -6933,9 +6536,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (s->vm86 && s->iopl != 3) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
- gen_pop_T0(s);
+ ot = gen_pop_T0(s);
if (s->cpl == 0) {
- if (s->dflag) {
+ if (dflag != MO_16) {
gen_helper_write_eflags(cpu_env, cpu_T[0],
tcg_const_i32((TF_MASK | AC_MASK |
ID_MASK | NT_MASK |
@@ -6950,7 +6553,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
}
} else {
if (s->cpl <= s->iopl) {
- if (s->dflag) {
+ if (dflag != MO_16) {
gen_helper_write_eflags(cpu_env, cpu_T[0],
tcg_const_i32((TF_MASK |
AC_MASK |
@@ -6967,7 +6570,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
& 0xffff));
}
} else {
- if (s->dflag) {
+ if (dflag != MO_16) {
gen_helper_write_eflags(cpu_env, cpu_T[0],
tcg_const_i32((TF_MASK | AC_MASK |
ID_MASK | NT_MASK)));
@@ -6979,7 +6582,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
}
}
}
- gen_pop_update(s);
+ gen_pop_update(s, ot);
set_cc_op(s, CC_OP_EFLAGS);
/* abort translation because TF/AC flag may change */
gen_jmp_im(s->pc - s->cs_base);
@@ -6989,7 +6592,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x9e: /* sahf */
if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM))
goto illegal_op;
- gen_op_mov_TN_reg(OT_BYTE, 0, R_AH);
+ gen_op_mov_v_reg(MO_8, cpu_T[0], R_AH);
gen_compute_eflags(s);
tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O);
tcg_gen_andi_tl(cpu_T[0], cpu_T[0], CC_S | CC_Z | CC_A | CC_P | CC_C);
@@ -7001,7 +6604,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_compute_eflags(s);
/* Note: gen_compute_eflags() only gives the condition codes */
tcg_gen_ori_tl(cpu_T[0], cpu_cc_src, 0x02);
- gen_op_mov_reg_T0(OT_BYTE, R_AH);
+ gen_op_mov_reg_v(MO_8, R_AH, cpu_T[0]);
break;
case 0xf5: /* cmc */
gen_compute_eflags(s);
@@ -7027,21 +6630,21 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/************************/
/* bit operations */
case 0x1ba: /* bt/bts/btr/btc Gv, im */
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
op = (modrm >> 3) & 7;
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
if (mod != 3) {
s->rip_offset = 1;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
}
/* load shift */
val = cpu_ldub_code(env, s->pc++);
- gen_op_movl_T1_im(val);
+ tcg_gen_movi_tl(cpu_T[1], val);
if (op < 4)
goto illegal_op;
op -= 4;
@@ -7058,22 +6661,22 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x1bb: /* btc */
op = 3;
do_btx:
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
mod = (modrm >> 6) & 3;
rm = (modrm & 7) | REX_B(s);
- gen_op_mov_TN_reg(OT_LONG, 1, reg);
+ gen_op_mov_v_reg(MO_32, cpu_T[1], reg);
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
/* specific case: we need to add a displacement */
gen_exts(ot, cpu_T[1]);
tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot);
tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, ot);
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
- gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_ld_v(s, ot, cpu_T[0], cpu_A0);
} else {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
}
bt_op:
tcg_gen_andi_tl(cpu_T[1], cpu_T[1], (1 << (3 + ot)) - 1);
@@ -7105,17 +6708,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
}
set_cc_op(s, CC_OP_SARB + ot);
if (op != 0) {
- if (mod != 3)
- gen_op_st_T0_A0(ot + s->mem_index);
- else
- gen_op_mov_reg_T0(ot, rm);
+ if (mod != 3) {
+ gen_op_st_v(s, ot, cpu_T[0], cpu_A0);
+ } else {
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ }
tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4);
tcg_gen_movi_tl(cpu_cc_dst, 0);
}
break;
case 0x1bc: /* bsf / tzcnt */
case 0x1bd: /* bsr / lzcnt */
- ot = dflag + OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
@@ -7164,7 +6768,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[0], cpu_cc_dst, cpu_tmp0,
cpu_regs[reg], cpu_T[0]);
}
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
break;
/************************/
/* bcd */
@@ -7314,17 +6918,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x62: /* bound */
if (CODE64(s))
goto illegal_op;
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = (modrm >> 3) & 7;
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
- gen_op_mov_TN_reg(ot, 0, reg);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_op_mov_v_reg(ot, cpu_T[0], reg);
+ gen_lea_modrm(env, s, modrm);
gen_jmp_im(pc_start - s->cs_base);
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
- if (ot == OT_WORD) {
+ if (ot == MO_16) {
gen_helper_boundw(cpu_env, cpu_A0, cpu_tmp2_i32);
} else {
gen_helper_boundl(cpu_env, cpu_A0, cpu_tmp2_i32);
@@ -7333,17 +6937,17 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x1c8 ... 0x1cf: /* bswap reg */
reg = (b & 7) | REX_B(s);
#ifdef TARGET_X86_64
- if (dflag == 2) {
- gen_op_mov_TN_reg(OT_QUAD, 0, reg);
+ if (dflag == MO_64) {
+ gen_op_mov_v_reg(MO_64, cpu_T[0], reg);
tcg_gen_bswap64_i64(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_QUAD, reg);
+ gen_op_mov_reg_v(MO_64, reg, cpu_T[0]);
} else
#endif
{
- gen_op_mov_TN_reg(OT_LONG, 0, reg);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], reg);
tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]);
tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_LONG, reg);
+ gen_op_mov_reg_v(MO_32, reg, cpu_T[0]);
}
break;
case 0xd6: /* salc */
@@ -7351,7 +6955,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
goto illegal_op;
gen_compute_eflags_c(s, cpu_T[0]);
tcg_gen_neg_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(OT_BYTE, R_EAX);
+ gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]);
break;
case 0xe0: /* loopnz */
case 0xe1: /* loopz */
@@ -7360,11 +6964,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
{
int l1, l2, l3;
- tval = (int8_t)insn_get(env, s, OT_BYTE);
+ tval = (int8_t)insn_get(env, s, MO_8);
next_eip = s->pc - s->cs_base;
tval += next_eip;
- if (s->dflag == 0)
+ if (dflag == MO_16) {
tval &= 0xffff;
+ }
l1 = gen_new_label();
l2 = gen_new_label();
@@ -7449,7 +7054,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
} else {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_sysexit(cpu_env, tcg_const_i32(dflag));
+ gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1));
gen_eob(s);
}
break;
@@ -7467,7 +7072,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
} else {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_sysret(cpu_env, tcg_const_i32(s->dflag));
+ gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1));
/* condition codes are modified only in long mode */
if (s->lma) {
set_cc_op(s, CC_OP_EFLAGS);
@@ -7501,9 +7106,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
goto illegal_op;
gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ);
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,ldt.selector));
- ot = OT_WORD;
- if (mod == 3)
- ot += s->dflag;
+ ot = mod == 3 ? dflag : MO_16;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
case 2: /* lldt */
@@ -7513,7 +7116,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE);
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
gen_jmp_im(pc_start - s->cs_base);
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
gen_helper_lldt(cpu_env, cpu_tmp2_i32);
@@ -7524,9 +7127,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
goto illegal_op;
gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ);
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,tr.selector));
- ot = OT_WORD;
- if (mod == 3)
- ot += s->dflag;
+ ot = mod == 3 ? dflag : MO_16;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
case 3: /* ltr */
@@ -7536,7 +7137,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE);
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
gen_jmp_im(pc_start - s->cs_base);
tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
gen_helper_ltr(cpu_env, cpu_tmp2_i32);
@@ -7546,7 +7147,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 5: /* verw */
if (!s->pe || s->vm86)
goto illegal_op;
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
gen_update_cc_op(s);
if (op == 4) {
gen_helper_verr(cpu_env, cpu_T[0]);
@@ -7569,14 +7170,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (mod == 3)
goto illegal_op;
gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit));
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0);
gen_add_A0_im(s, 2);
tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.base));
- if (!s->dflag)
- gen_op_andl_T0_im(0xffffff);
- gen_op_st_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
+ if (dflag == MO_16) {
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff);
+ }
+ gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0);
break;
case 1:
if (mod == 3) {
@@ -7587,16 +7189,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
goto illegal_op;
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
-#ifdef TARGET_X86_64
- if (s->aflag == 2) {
- gen_op_movq_A0_reg(R_EAX);
- } else
-#endif
- {
- gen_op_movl_A0_reg(R_EAX);
- if (s->aflag == 0)
- gen_op_andl_A0_ffff();
- }
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]);
+ gen_extu(s->aflag, cpu_A0);
gen_add_A0_ds_seg(s);
gen_helper_monitor(cpu_env, cpu_A0);
break;
@@ -7632,14 +7226,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
}
} else { /* sidt */
gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit));
- gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0);
gen_add_A0_im(s, 2);
tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.base));
- if (!s->dflag)
- gen_op_andl_T0_im(0xffffff);
- gen_op_st_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
+ if (dflag == MO_16) {
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff);
+ }
+ gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0);
}
break;
case 2: /* lgdt */
@@ -7655,7 +7250,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
break;
} else {
- gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag),
+ gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1),
tcg_const_i32(s->pc - pc_start));
tcg_gen_exit_tb(0);
s->is_jmp = DISAS_TB_JUMP;
@@ -7673,7 +7268,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
break;
} else {
- gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag));
+ gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1));
}
break;
case 3: /* VMSAVE */
@@ -7683,7 +7278,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
break;
} else {
- gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag));
+ gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1));
}
break;
case 4: /* STGI */
@@ -7722,7 +7317,8 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
break;
} else {
- gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag));
+ gen_helper_invlpga(cpu_env,
+ tcg_const_i32(s->aflag - 1));
}
break;
default:
@@ -7733,12 +7329,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
} else {
gen_svm_check_intercept(s, pc_start,
op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_T1_A0(OT_WORD + s->mem_index);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, MO_16, cpu_T[1], cpu_A0);
gen_add_A0_im(s, 2);
- gen_op_ld_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
- if (!s->dflag)
- gen_op_andl_T0_im(0xffffff);
+ gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0);
+ if (dflag == MO_16) {
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff);
+ }
if (op == 2) {
tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,gdt.base));
tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,gdt.limit));
@@ -7755,14 +7352,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
#else
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0]));
#endif
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 1);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 1);
break;
case 6: /* lmsw */
if (s->cpl != 0) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else {
gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0);
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
gen_helper_lmsw(cpu_env, cpu_T[0]);
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
@@ -7775,7 +7372,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
} else {
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
gen_helper_invlpg(cpu_env, cpu_A0);
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
@@ -7839,7 +7436,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if (CODE64(s)) {
int d_ot;
/* d_ot is the size of destination */
- d_ot = dflag + OT_WORD;
+ d_ot = dflag;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
@@ -7847,19 +7444,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
rm = (modrm & 7) | REX_B(s);
if (mod == 3) {
- gen_op_mov_TN_reg(OT_LONG, 0, rm);
+ gen_op_mov_v_reg(MO_32, cpu_T[0], rm);
/* sign extend */
- if (d_ot == OT_QUAD)
+ if (d_ot == MO_64) {
tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
- gen_op_mov_reg_T0(d_ot, reg);
- } else {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- if (d_ot == OT_QUAD) {
- gen_op_lds_T0_A0(OT_LONG + s->mem_index);
- } else {
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
}
- gen_op_mov_reg_T0(d_ot, reg);
+ gen_op_mov_reg_v(d_ot, reg, cpu_T[0]);
+ } else {
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, MO_32 | MO_SIGN, cpu_T[0], cpu_A0);
+ gen_op_mov_reg_v(d_ot, reg, cpu_T[0]);
}
} else
#endif
@@ -7872,14 +7466,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
t0 = tcg_temp_local_new();
t1 = tcg_temp_local_new();
t2 = tcg_temp_local_new();
- ot = OT_WORD;
+ ot = MO_16;
modrm = cpu_ldub_code(env, s->pc++);
reg = (modrm >> 3) & 7;
mod = (modrm >> 6) & 3;
rm = modrm & 7;
if (mod != 3) {
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
- gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
+ gen_lea_modrm(env, s, modrm);
+ gen_op_ld_v(s, ot, t0, cpu_A0);
a0 = tcg_temp_local_new();
tcg_gen_mov_tl(a0, cpu_A0);
} else {
@@ -7897,7 +7491,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
tcg_gen_movi_tl(t2, CC_Z);
gen_set_label(label1);
if (mod != 3) {
- gen_op_st_v(ot + s->mem_index, t0, a0);
+ gen_op_st_v(s, ot, t0, a0);
tcg_temp_free(a0);
} else {
gen_op_mov_reg_v(ot, rm, t0);
@@ -7917,10 +7511,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
TCGv t0;
if (!s->pe || s->vm86)
goto illegal_op;
- ot = dflag ? OT_LONG : OT_WORD;
+ ot = dflag != MO_16 ? MO_32 : MO_16;
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
- gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
t0 = tcg_temp_local_new();
gen_update_cc_op(s);
if (b == 0x102) {
@@ -7948,7 +7542,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 3: /* prefetchnt0 */
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
/* nothing more to do */
break;
default: /* nop (multi byte) */
@@ -7974,9 +7568,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
rm = (modrm & 7) | REX_B(s);
reg = ((modrm >> 3) & 7) | rex_r;
if (CODE64(s))
- ot = OT_QUAD;
+ ot = MO_64;
else
- ot = OT_LONG;
+ ot = MO_32;
if ((prefixes & PREFIX_LOCK) && (reg == 0) &&
(s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) {
reg = 8;
@@ -7990,14 +7584,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
if (b & 2) {
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
gen_helper_write_crN(cpu_env, tcg_const_i32(reg),
cpu_T[0]);
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
} else {
gen_helper_read_crN(cpu_T[0], cpu_env, tcg_const_i32(reg));
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
}
break;
default:
@@ -8019,22 +7613,22 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
rm = (modrm & 7) | REX_B(s);
reg = ((modrm >> 3) & 7) | rex_r;
if (CODE64(s))
- ot = OT_QUAD;
+ ot = MO_64;
else
- ot = OT_LONG;
+ ot = MO_32;
/* XXX: do it dynamically with CR4.DE bit */
if (reg == 4 || reg == 5 || reg >= 8)
goto illegal_op;
if (b & 2) {
gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg);
- gen_op_mov_TN_reg(ot, 0, rm);
+ gen_op_mov_v_reg(ot, cpu_T[0], rm);
gen_helper_movl_drN_T0(cpu_env, tcg_const_i32(reg), cpu_T[0]);
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
} else {
gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg);
tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,dr[reg]));
- gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
}
}
break;
@@ -8053,7 +7647,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
case 0x1c3: /* MOVNTI reg, mem */
if (!(s->cpuid_features & CPUID_SSE2))
goto illegal_op;
- ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ ot = mo_64_32(dflag);
modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3;
if (mod == 3)
@@ -8075,10 +7669,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
break;
}
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32((s->dflag == 2)));
+ gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64));
break;
case 1: /* fxrstor */
if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) ||
@@ -8088,11 +7682,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
break;
}
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
gen_update_cc_op(s);
gen_jmp_im(pc_start - s->cs_base);
- gen_helper_fxrstor(cpu_env, cpu_A0,
- tcg_const_i32((s->dflag == 2)));
+ gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64));
break;
case 2: /* ldmxcsr */
case 3: /* stmxcsr */
@@ -8103,14 +7696,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) ||
mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
if (op == 2) {
- gen_op_ld_T0_A0(OT_LONG + s->mem_index);
- tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0,
+ s->mem_index, MO_LEUL);
gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32);
} else {
tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
- gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0);
}
break;
case 5: /* lfence */
@@ -8128,7 +7721,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
/* clflush */
if (!(s->cpuid_features & CPUID_CLFLUSH))
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
}
break;
default:
@@ -8140,7 +7733,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
mod = (modrm >> 6) & 3;
if (mod == 3)
goto illegal_op;
- gen_lea_modrm(env, s, modrm, &reg_addr, &offset_addr);
+ gen_lea_modrm(env, s, modrm);
/* ignore for now */
break;
case 0x1aa: /* rsm */
@@ -8162,16 +7755,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
modrm = cpu_ldub_code(env, s->pc++);
reg = ((modrm >> 3) & 7) | rex_r;
- if (s->prefix & PREFIX_DATA)
- ot = OT_WORD;
- else if (s->dflag != 2)
- ot = OT_LONG;
- else
- ot = OT_QUAD;
+ if (s->prefix & PREFIX_DATA) {
+ ot = MO_16;
+ } else {
+ ot = mo_64_32(dflag);
+ }
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
gen_helper_popcnt(cpu_T[0], cpu_env, cpu_T[0], tcg_const_i32(ot));
- gen_op_mov_reg_T0(ot, reg);
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
set_cc_op(s, CC_OP_EFLAGS);
break;
@@ -8205,6 +7797,37 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
void optimize_flags_init(void)
{
+ static const char reg_names[CPU_NB_REGS][4] = {
+#ifdef TARGET_X86_64
+ [R_EAX] = "rax",
+ [R_EBX] = "rbx",
+ [R_ECX] = "rcx",
+ [R_EDX] = "rdx",
+ [R_ESI] = "rsi",
+ [R_EDI] = "rdi",
+ [R_EBP] = "rbp",
+ [R_ESP] = "rsp",
+ [8] = "r8",
+ [9] = "r9",
+ [10] = "r10",
+ [11] = "r11",
+ [12] = "r12",
+ [13] = "r13",
+ [14] = "r14",
+ [15] = "r15",
+#else
+ [R_EAX] = "eax",
+ [R_EBX] = "ebx",
+ [R_ECX] = "ecx",
+ [R_EDX] = "edx",
+ [R_ESI] = "esi",
+ [R_EDI] = "edi",
+ [R_EBP] = "ebp",
+ [R_ESP] = "esp",
+#endif
+ };
+ int i;
+
cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUX86State, cc_op), "cc_op");
@@ -8215,57 +7838,11 @@ void optimize_flags_init(void)
cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src2),
"cc_src2");
-#ifdef TARGET_X86_64
- cpu_regs[R_EAX] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EAX]), "rax");
- cpu_regs[R_ECX] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ECX]), "rcx");
- cpu_regs[R_EDX] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EDX]), "rdx");
- cpu_regs[R_EBX] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EBX]), "rbx");
- cpu_regs[R_ESP] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ESP]), "rsp");
- cpu_regs[R_EBP] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EBP]), "rbp");
- cpu_regs[R_ESI] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ESI]), "rsi");
- cpu_regs[R_EDI] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EDI]), "rdi");
- cpu_regs[8] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[8]), "r8");
- cpu_regs[9] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[9]), "r9");
- cpu_regs[10] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[10]), "r10");
- cpu_regs[11] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[11]), "r11");
- cpu_regs[12] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[12]), "r12");
- cpu_regs[13] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[13]), "r13");
- cpu_regs[14] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[14]), "r14");
- cpu_regs[15] = tcg_global_mem_new_i64(TCG_AREG0,
- offsetof(CPUX86State, regs[15]), "r15");
-#else
- cpu_regs[R_EAX] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EAX]), "eax");
- cpu_regs[R_ECX] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ECX]), "ecx");
- cpu_regs[R_EDX] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EDX]), "edx");
- cpu_regs[R_EBX] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EBX]), "ebx");
- cpu_regs[R_ESP] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ESP]), "esp");
- cpu_regs[R_EBP] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EBP]), "ebp");
- cpu_regs[R_ESI] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_ESI]), "esi");
- cpu_regs[R_EDI] = tcg_global_mem_new_i32(TCG_AREG0,
- offsetof(CPUX86State, regs[R_EDI]), "edi");
-#endif
+ for (i = 0; i < CPU_NB_REGS; ++i) {
+ cpu_regs[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUX86State, regs[i]),
+ reg_names[i]);
+ }
}
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
@@ -8311,7 +7888,7 @@ static inline void gen_intermediate_code_internal(X86CPU *cpu,
/* select memory access functions */
dc->mem_index = 0;
if (flags & HF_SOFTMMU_MASK) {
- dc->mem_index = (cpu_mmu_index(env) + 1) << 2;
+ dc->mem_index = cpu_mmu_index(env);
}
dc->cpuid_features = env->features[FEAT_1_EDX];
dc->cpuid_ext_features = env->features[FEAT_1_ECX];
diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c
index 0ef9aa4b74..f108c0b521 100644
--- a/target-microblaze/cpu.c
+++ b/target-microblaze/cpu.c
@@ -4,6 +4,7 @@
* Copyright (c) 2009 Edgar E. Iglesias
* Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd.
* Copyright (c) 2012 SUSE LINUX Products GmbH
+ * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -33,6 +34,21 @@ static void mb_cpu_set_pc(CPUState *cs, vaddr value)
cpu->env.sregs[SR_PC] = value;
}
+#ifndef CONFIG_USER_ONLY
+static void microblaze_cpu_set_irq(void *opaque, int irq, int level)
+{
+ MicroBlazeCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+ if (level) {
+ cpu_interrupt(cs, type);
+ } else {
+ cpu_reset_interrupt(cs, type);
+ }
+}
+#endif
+
/* CPUClass::reset() */
static void mb_cpu_reset(CPUState *s)
{
@@ -111,6 +127,11 @@ static void mb_cpu_initfn(Object *obj)
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+#ifndef CONFIG_USER_ONLY
+ /* Inbound IRQ and FIR lines */
+ qdev_init_gpio_in(DEVICE(cpu), microblaze_cpu_set_irq, 2);
+#endif
+
if (tcg_enabled() && !tcg_initialized) {
tcg_initialized = true;
mb_tcg_init();
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
index e1415f043c..1df014e92e 100644
--- a/target-microblaze/cpu.h
+++ b/target-microblaze/cpu.h
@@ -48,6 +48,10 @@ typedef struct CPUMBState CPUMBState;
/* MicroBlaze-specific interrupt pending bits. */
#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
+/* Meanings of the MBCPU object's two inbound GPIO lines */
+#define MB_CPU_IRQ 0
+#define MB_CPU_FIR 1
+
/* Register aliases. R0 - R15 */
#define R_SP 1
#define SR_PC 0
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index 9edcb67e66..270138c6d2 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -864,26 +864,6 @@ static void dec_imm(DisasContext *dc)
dc->clear_imm = 0;
}
-static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr,
- unsigned int size, bool exclusive)
-{
- int mem_index = cpu_mmu_index(dc->env);
-
- if (size == 1) {
- tcg_gen_qemu_ld8u(dst, addr, mem_index);
- } else if (size == 2) {
- tcg_gen_qemu_ld16u(dst, addr, mem_index);
- } else if (size == 4) {
- tcg_gen_qemu_ld32u(dst, addr, mem_index);
- } else
- cpu_abort(dc->env, "Incorrect load size %d\n", size);
-
- if (exclusive) {
- tcg_gen_mov_tl(env_res_addr, addr);
- tcg_gen_mov_tl(env_res_val, dst);
- }
-}
-
static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
{
unsigned int extimm = dc->tb_flags & IMM_FLAG;
@@ -935,35 +915,22 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
return t;
}
-static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size)
-{
- if (size == 4) {
- tcg_gen_bswap32_tl(dst, src);
- } else if (size == 2) {
- TCGv t = tcg_temp_new();
-
- /* bswap16 assumes the high bits are zero. */
- tcg_gen_andi_tl(t, src, 0xffff);
- tcg_gen_bswap16_tl(dst, t);
- tcg_temp_free(t);
- } else {
- /* Ignore.
- cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size);
- */
- }
-}
-
static void dec_load(DisasContext *dc)
{
- TCGv t, *addr;
+ TCGv t, v, *addr;
unsigned int size, rev = 0, ex = 0;
+ TCGMemOp mop;
- size = 1 << (dc->opcode & 3);
-
+ mop = dc->opcode & 3;
+ size = 1 << mop;
if (!dc->type_b) {
rev = (dc->ir >> 9) & 1;
ex = (dc->ir >> 10) & 1;
}
+ mop |= MO_TE;
+ if (rev) {
+ mop ^= MO_BSWAP;
+ }
if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
&& (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
@@ -1044,40 +1011,30 @@ static void dec_load(DisasContext *dc)
sync_jmpstate(dc);
/* Verify alignment if needed. */
- if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
- TCGv v = tcg_temp_new();
-
- /*
- * Microblaze gives MMU faults priority over faults due to
- * unaligned addresses. That's why we speculatively do the load
- * into v. If the load succeeds, we verify alignment of the
- * address and if that succeeds we write into the destination reg.
- */
- gen_load(dc, v, *addr, size, ex);
+ /*
+ * Microblaze gives MMU faults priority over faults due to
+ * unaligned addresses. That's why we speculatively do the load
+ * into v. If the load succeeds, we verify alignment of the
+ * address and if that succeeds we write into the destination reg.
+ */
+ v = tcg_temp_new();
+ tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(dc->env), mop);
+ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd),
tcg_const_tl(0), tcg_const_tl(size - 1));
- if (dc->rd) {
- if (rev) {
- dec_byteswap(dc, cpu_R[dc->rd], v, size);
- } else {
- tcg_gen_mov_tl(cpu_R[dc->rd], v);
- }
- }
- tcg_temp_free(v);
- } else {
- if (dc->rd) {
- gen_load(dc, cpu_R[dc->rd], *addr, size, ex);
- if (rev) {
- dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size);
- }
- } else {
- /* We are loading into r0, no need to reverse. */
- gen_load(dc, env_imm, *addr, size, ex);
- }
}
+ if (ex) {
+ tcg_gen_mov_tl(env_res_addr, *addr);
+ tcg_gen_mov_tl(env_res_val, v);
+ }
+ if (dc->rd) {
+ tcg_gen_mov_tl(cpu_R[dc->rd], v);
+ }
+ tcg_temp_free(v);
+
if (ex) { /* lwx */
/* no support for for AXI exclusive so always clear C */
write_carryi(dc, 0);
@@ -1087,32 +1044,23 @@ static void dec_load(DisasContext *dc)
tcg_temp_free(t);
}
-static void gen_store(DisasContext *dc, TCGv addr, TCGv val,
- unsigned int size)
-{
- int mem_index = cpu_mmu_index(dc->env);
-
- if (size == 1)
- tcg_gen_qemu_st8(val, addr, mem_index);
- else if (size == 2) {
- tcg_gen_qemu_st16(val, addr, mem_index);
- } else if (size == 4) {
- tcg_gen_qemu_st32(val, addr, mem_index);
- } else
- cpu_abort(dc->env, "Incorrect store size %d\n", size);
-}
-
static void dec_store(DisasContext *dc)
{
TCGv t, *addr, swx_addr;
int swx_skip = 0;
unsigned int size, rev = 0, ex = 0;
+ TCGMemOp mop;
- size = 1 << (dc->opcode & 3);
+ mop = dc->opcode & 3;
+ size = 1 << mop;
if (!dc->type_b) {
rev = (dc->ir >> 9) & 1;
ex = (dc->ir >> 10) & 1;
}
+ mop |= MO_TE;
+ if (rev) {
+ mop ^= MO_BSWAP;
+ }
if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
&& (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
@@ -1148,7 +1096,7 @@ static void dec_store(DisasContext *dc)
this compare and the following write to be atomic. For user
emulation we need to add atomicity between threads. */
tval = tcg_temp_new();
- gen_load(dc, tval, swx_addr, 4, false);
+ tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(dc->env), MO_TEUL);
tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip);
write_carryi(dc, 0);
tcg_temp_free(tval);
@@ -1197,25 +1145,8 @@ static void dec_store(DisasContext *dc)
cpu_abort(dc->env, "Invalid reverse size\n");
break;
}
-
- if (size != 1) {
- TCGv bs_data = tcg_temp_new();
- dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
- gen_store(dc, *addr, bs_data, size);
- tcg_temp_free(bs_data);
- } else {
- gen_store(dc, *addr, cpu_R[dc->rd], size);
- }
- } else {
- if (rev) {
- TCGv bs_data = tcg_temp_new();
- dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
- gen_store(dc, *addr, bs_data, size);
- tcg_temp_free(bs_data);
- } else {
- gen_store(dc, *addr, cpu_R[dc->rd], size);
- }
}
+ tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(dc->env), mop);
/* Verify alignment if needed. */
if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
diff --git a/target-mips/translate.c b/target-mips/translate.c
index e30273438a..ef0a2c36b0 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -1606,12 +1606,12 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
switch (opc) {
#if defined(TARGET_MIPS64)
case OPC_LWU:
- tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
gen_store_gpr(t0, rt);
opn = "lwu";
break;
case OPC_LD:
- tcg_gen_qemu_ld64(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
gen_store_gpr(t0, rt);
opn = "ld";
break;
@@ -1629,7 +1629,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
#endif
tcg_gen_shli_tl(t1, t1, 3);
tcg_gen_andi_tl(t0, t0, ~7);
- tcg_gen_qemu_ld64(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
tcg_gen_shl_tl(t0, t0, t1);
tcg_gen_xori_tl(t1, t1, 63);
t2 = tcg_const_tl(0x7fffffffffffffffull);
@@ -1650,7 +1650,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
#endif
tcg_gen_shli_tl(t1, t1, 3);
tcg_gen_andi_tl(t0, t0, ~7);
- tcg_gen_qemu_ld64(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
tcg_gen_shr_tl(t0, t0, t1);
tcg_gen_xori_tl(t1, t1, 63);
t2 = tcg_const_tl(0xfffffffffffffffeull);
@@ -1667,7 +1667,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
t1 = tcg_const_tl(pc_relative_pc(ctx));
gen_op_addr_add(ctx, t0, t0, t1);
tcg_temp_free(t1);
- tcg_gen_qemu_ld64(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
gen_store_gpr(t0, rt);
opn = "ldpc";
break;
@@ -1676,32 +1676,32 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
t1 = tcg_const_tl(pc_relative_pc(ctx));
gen_op_addr_add(ctx, t0, t0, t1);
tcg_temp_free(t1);
- tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t0, rt);
opn = "lwpc";
break;
case OPC_LW:
- tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t0, rt);
opn = "lw";
break;
case OPC_LH:
- tcg_gen_qemu_ld16s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW);
gen_store_gpr(t0, rt);
opn = "lh";
break;
case OPC_LHU:
- tcg_gen_qemu_ld16u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW);
gen_store_gpr(t0, rt);
opn = "lhu";
break;
case OPC_LB:
- tcg_gen_qemu_ld8s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB);
gen_store_gpr(t0, rt);
opn = "lb";
break;
case OPC_LBU:
- tcg_gen_qemu_ld8u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB);
gen_store_gpr(t0, rt);
opn = "lbu";
break;
@@ -1713,7 +1713,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
#endif
tcg_gen_shli_tl(t1, t1, 3);
tcg_gen_andi_tl(t0, t0, ~3);
- tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
tcg_gen_shl_tl(t0, t0, t1);
tcg_gen_xori_tl(t1, t1, 31);
t2 = tcg_const_tl(0x7fffffffull);
@@ -1735,7 +1735,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
#endif
tcg_gen_shli_tl(t1, t1, 3);
tcg_gen_andi_tl(t0, t0, ~3);
- tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
tcg_gen_shr_tl(t0, t0, t1);
tcg_gen_xori_tl(t1, t1, 31);
t2 = tcg_const_tl(0xfffffffeull);
@@ -1774,7 +1774,7 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
switch (opc) {
#if defined(TARGET_MIPS64)
case OPC_SD:
- tcg_gen_qemu_st64(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ);
opn = "sd";
break;
case OPC_SDL:
@@ -1789,15 +1789,15 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
break;
#endif
case OPC_SW:
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
opn = "sw";
break;
case OPC_SH:
- tcg_gen_qemu_st16(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW);
opn = "sh";
break;
case OPC_SB:
- tcg_gen_qemu_st8(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_8);
opn = "sb";
break;
case OPC_SWL:
@@ -1868,9 +1868,7 @@ 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_ld32s(t0, t0, ctx->mem_idx);
- tcg_gen_trunc_tl_i32(fp0, t0);
+ tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL);
gen_store_fpr32(fp0, ft);
tcg_temp_free_i32(fp0);
}
@@ -1879,12 +1877,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
case OPC_SWC1:
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- TCGv t1 = tcg_temp_new();
-
gen_load_fpr32(fp0, ft);
- tcg_gen_extu_i32_tl(t1, fp0);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
- tcg_temp_free(t1);
+ tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL);
tcg_temp_free_i32(fp0);
}
opn = "swc1";
@@ -1892,8 +1886,7 @@ 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_ld64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
gen_store_fpr64(ctx, fp0, ft);
tcg_temp_free_i64(fp0);
}
@@ -1902,9 +1895,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
case OPC_SDC1:
{
TCGv_i64 fp0 = tcg_temp_new_i64();
-
gen_load_fpr64(ctx, fp0, ft);
- tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
tcg_temp_free_i64(fp0);
}
opn = "sdc1";
@@ -8652,7 +8644,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
tcg_gen_trunc_tl_i32(fp0, t0);
gen_store_fpr32(fp0, fd);
tcg_temp_free_i32(fp0);
@@ -8664,8 +8656,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
check_cp1_registers(ctx, fd);
{
TCGv_i64 fp0 = tcg_temp_new_i64();
-
- tcg_gen_qemu_ld64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
gen_store_fpr64(ctx, fp0, fd);
tcg_temp_free_i64(fp0);
}
@@ -8677,7 +8668,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
{
TCGv_i64 fp0 = tcg_temp_new_i64();
- tcg_gen_qemu_ld64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
gen_store_fpr64(ctx, fp0, fd);
tcg_temp_free_i64(fp0);
}
@@ -8687,13 +8678,9 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
check_cop1x(ctx);
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- TCGv t1 = tcg_temp_new();
-
gen_load_fpr32(fp0, fs);
- tcg_gen_extu_i32_tl(t1, fp0);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL);
tcg_temp_free_i32(fp0);
- tcg_temp_free(t1);
}
opn = "swxc1";
store = 1;
@@ -8703,9 +8690,8 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
check_cp1_registers(ctx, fs);
{
TCGv_i64 fp0 = tcg_temp_new_i64();
-
gen_load_fpr64(ctx, fp0, fs);
- tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
tcg_temp_free_i64(fp0);
}
opn = "sdxc1";
@@ -8716,9 +8702,8 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
tcg_gen_andi_tl(t0, t0, ~0x7);
{
TCGv_i64 fp0 = tcg_temp_new_i64();
-
gen_load_fpr64(ctx, fp0, fs);
- tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
tcg_temp_free_i64(fp0);
}
opn = "suxc1";
@@ -9286,30 +9271,30 @@ static void gen_mips16_save (DisasContext *ctx,
case 4:
gen_base_offset_addr(ctx, t0, 29, 12);
gen_load_gpr(t1, 7);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
/* Fall through */
case 3:
gen_base_offset_addr(ctx, t0, 29, 8);
gen_load_gpr(t1, 6);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
/* Fall through */
case 2:
gen_base_offset_addr(ctx, t0, 29, 4);
gen_load_gpr(t1, 5);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
/* Fall through */
case 1:
gen_base_offset_addr(ctx, t0, 29, 0);
gen_load_gpr(t1, 4);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
}
gen_load_gpr(t0, 29);
-#define DECR_AND_STORE(reg) do { \
- tcg_gen_subi_tl(t0, t0, 4); \
- gen_load_gpr(t1, reg); \
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx); \
+#define DECR_AND_STORE(reg) do { \
+ tcg_gen_subi_tl(t0, t0, 4); \
+ gen_load_gpr(t1, reg); \
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); \
} while (0)
if (do_ra) {
@@ -9407,10 +9392,10 @@ static void gen_mips16_restore (DisasContext *ctx,
tcg_gen_addi_tl(t0, cpu_gpr[29], framesize);
-#define DECR_AND_LOAD(reg) do { \
- tcg_gen_subi_tl(t0, t0, 4); \
- tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx); \
- gen_store_gpr(t1, reg); \
+#define DECR_AND_LOAD(reg) do { \
+ tcg_gen_subi_tl(t0, t0, 4); \
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); \
+ gen_store_gpr(t1, reg); \
} while (0)
if (do_ra) {
@@ -10935,7 +10920,7 @@ static void gen_ldxs (DisasContext *ctx, int base, int index, int rd)
gen_op_addr_add(ctx, t0, t1, t0);
}
- tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t1, rd);
tcg_temp_free(t0);
@@ -10964,21 +10949,21 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd,
generate_exception(ctx, EXCP_RI);
return;
}
- tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t1, rd);
tcg_gen_movi_tl(t1, 4);
gen_op_addr_add(ctx, t0, t0, t1);
- tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t1, rd+1);
opn = "lwp";
break;
case SWP:
gen_load_gpr(t1, rd);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
tcg_gen_movi_tl(t1, 4);
gen_op_addr_add(ctx, t0, t0, t1);
gen_load_gpr(t1, rd+1);
- tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
opn = "swp";
break;
#ifdef TARGET_MIPS64
@@ -10987,21 +10972,21 @@ static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd,
generate_exception(ctx, EXCP_RI);
return;
}
- tcg_gen_qemu_ld64(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ);
gen_store_gpr(t1, rd);
tcg_gen_movi_tl(t1, 8);
gen_op_addr_add(ctx, t0, t0, t1);
- tcg_gen_qemu_ld64(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ);
gen_store_gpr(t1, rd+1);
opn = "ldp";
break;
case SDP:
gen_load_gpr(t1, rd);
- tcg_gen_qemu_st64(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ);
tcg_gen_movi_tl(t1, 8);
gen_op_addr_add(ctx, t0, t0, t1);
gen_load_gpr(t1, rd+1);
- tcg_gen_qemu_st64(t1, t0, ctx->mem_idx);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ);
opn = "sdp";
break;
#endif
@@ -12672,23 +12657,23 @@ static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc,
switch (opc) {
case OPC_LBUX:
- tcg_gen_qemu_ld8u(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB);
gen_store_gpr(t0, rd);
opn = "lbux";
break;
case OPC_LHX:
- tcg_gen_qemu_ld16s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW);
gen_store_gpr(t0, rd);
opn = "lhx";
break;
case OPC_LWX:
- tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
gen_store_gpr(t0, rd);
opn = "lwx";
break;
#if defined(TARGET_MIPS64)
case OPC_LDX:
- tcg_gen_qemu_ld64(t0, t0, ctx->mem_idx);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
gen_store_gpr(t0, rd);
opn = "ldx";
break;
diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c
index 91c60ebaae..b381477d29 100644
--- a/target-openrisc/translate.c
+++ b/target-openrisc/translate.c
@@ -112,7 +112,7 @@ void openrisc_translate_init(void)
}
}
-/* Writeback SR_F transaltion-space to execution-space. */
+/* Writeback SR_F translation space to execution space. */
static inline void wb_SR_F(void)
{
int label;
diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c
index 8dea560383..7c9466fc07 100644
--- a/target-ppc/cpu-models.c
+++ b/target-ppc/cpu-models.c
@@ -44,6 +44,7 @@
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \
\
pcc->pvr = _pvr; \
+ pcc->pvr_mask = CPU_POWERPC_DEFAULT_MASK; \
pcc->svr = _svr; \
dc->desc = _desc; \
} \
@@ -1139,7 +1140,7 @@
"POWER7 v2.1")
POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7,
"POWER7 v2.3")
- POWERPC_DEF("POWER7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7,
+ POWERPC_DEF("POWER7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7P,
"POWER7+ v2.1")
POWERPC_DEF("POWER8_v1.0", CPU_POWERPC_POWER8_v10, POWER8,
"POWER8 v1.0")
diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h
index d9145d147f..49ba4a4522 100644
--- a/target-ppc/cpu-models.h
+++ b/target-ppc/cpu-models.h
@@ -39,6 +39,7 @@ extern PowerPCCPUAlias ppc_cpu_aliases[];
/*****************************************************************************/
/* PVR definitions for most known PowerPC */
enum {
+ CPU_POWERPC_DEFAULT_MASK = 0xFFFFFFFF,
/* PowerPC 401 family */
/* Generic PowerPC 401 */
#define CPU_POWERPC_401 CPU_POWERPC_401G2
@@ -552,10 +553,16 @@ enum {
CPU_POWERPC_POWER6 = 0x003E0000,
CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */
CPU_POWERPC_POWER6A = 0x0F000002,
+ CPU_POWERPC_POWER7_BASE = 0x003F0000,
+ CPU_POWERPC_POWER7_MASK = 0xFFFF0000,
CPU_POWERPC_POWER7_v20 = 0x003F0200,
CPU_POWERPC_POWER7_v21 = 0x003F0201,
CPU_POWERPC_POWER7_v23 = 0x003F0203,
+ CPU_POWERPC_POWER7P_BASE = 0x004A0000,
+ CPU_POWERPC_POWER7P_MASK = 0xFFFF0000,
CPU_POWERPC_POWER7P_v21 = 0x004A0201,
+ CPU_POWERPC_POWER8_BASE = 0x004B0000,
+ CPU_POWERPC_POWER8_MASK = 0xFFFF0000,
CPU_POWERPC_POWER8_v10 = 0x004B0100,
CPU_POWERPC_970 = 0x00390202,
CPU_POWERPC_970FX_v10 = 0x00391100,
diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h
index 827e5dd0e1..72b22329b0 100644
--- a/target-ppc/cpu-qom.h
+++ b/target-ppc/cpu-qom.h
@@ -54,6 +54,7 @@ typedef struct PowerPCCPUClass {
void (*parent_reset)(CPUState *cpu);
uint32_t pvr;
+ uint32_t pvr_mask;
uint32_t svr;
uint64_t insns_flags;
uint64_t insns_flags2;
@@ -99,6 +100,7 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
#define ENV_OFFSET offsetof(PowerPCCPU, env)
PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr);
+PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr);
void ppc_cpu_do_interrupt(CPUState *cpu);
void ppc_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 26acdba847..bb847676a5 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -236,6 +236,8 @@ enum {
POWERPC_EXCP_NMEXTBR = 91, /* Non maskable external breakpoint */
POWERPC_EXCP_ITLBE = 92, /* Instruction TLB error */
POWERPC_EXCP_DTLBE = 93, /* Data TLB error */
+ /* VSX Unavailable (Power ISA 2.06 and later) */
+ POWERPC_EXCP_VSXU = 94, /* VSX Unavailable */
/* EOL */
POWERPC_EXCP_NB = 96,
/* QEMU exceptions: used internally during code translation */
@@ -427,6 +429,7 @@ struct ppc_slb_t {
#define MSR_VR 25 /* altivec available x hflags */
#define MSR_SPE 25 /* SPE enable for BookE x hflags */
#define MSR_AP 23 /* Access privilege state on 602 hflags */
+#define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */
#define MSR_SA 22 /* Supervisor access mode on 602 hflags */
#define MSR_KEY 19 /* key bit on 603e */
#define MSR_POW 18 /* Power management */
@@ -467,6 +470,7 @@ struct ppc_slb_t {
#define msr_vr ((env->msr >> MSR_VR) & 1)
#define msr_spe ((env->msr >> MSR_SPE) & 1)
#define msr_ap ((env->msr >> MSR_AP) & 1)
+#define msr_vsx ((env->msr >> MSR_VSX) & 1)
#define msr_sa ((env->msr >> MSR_SA) & 1)
#define msr_key ((env->msr >> MSR_KEY) & 1)
#define msr_pow ((env->msr >> MSR_POW) & 1)
@@ -549,6 +553,8 @@ enum {
POWERPC_FLAG_BUS_CLK = 0x00020000,
/* Has CFAR */
POWERPC_FLAG_CFAR = 0x00040000,
+ /* Has VSX */
+ POWERPC_FLAG_VSX = 0x00080000,
};
/*****************************************************************************/
@@ -1870,7 +1876,8 @@ enum {
/* Book I 2.05 PowerPC specification */
PPC2_ISA205 = 0x0000000000000020ULL,
-#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_DBRX | PPC2_ISA205)
+#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
+ PPC2_ISA205)
};
/*****************************************************************************/
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index c959460f70..26c57d9a34 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -390,6 +390,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
new_msr |= (target_ulong)MSR_HVB;
}
goto store_current;
+ case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
+ if (lpes1 == 0) {
+ new_msr |= (target_ulong)MSR_HVB;
+ }
+ goto store_current;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
goto store_next;
diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h
index a6d5e2fe2f..c02e8da4e4 100644
--- a/target-ppc/helper_regs.h
+++ b/target-ppc/helper_regs.h
@@ -56,7 +56,7 @@ static inline void hreg_compute_hflags(CPUPPCState *env)
/* We 'forget' FE0 & FE1: we'll never generate imprecise exceptions */
hflags_mask = (1 << MSR_VR) | (1 << MSR_AP) | (1 << MSR_SA) |
(1 << MSR_PR) | (1 << MSR_FP) | (1 << MSR_SE) | (1 << MSR_BE) |
- (1 << MSR_LE);
+ (1 << MSR_LE) | (1 << MSR_VSX);
hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF) | MSR_HVB;
hreg_compute_mem_idx(env);
env->hflags = env->msr & hflags_mask;
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index b77ce5e94c..781b72f1ea 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -1745,6 +1745,7 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
uint32_t icache_size = kvmppc_read_int_cpu_dt("i-cache-size");
/* Now fix up the class with information we can query from the host */
+ pcc->pvr = mfpvr();
if (vmx != -1) {
/* Only override when we know what the host supports */
@@ -1795,6 +1796,9 @@ static int kvm_ppc_register_host_cpu_type(void)
pvr_pcc = ppc_cpu_class_by_pvr(host_pvr);
if (pvr_pcc == NULL) {
+ pvr_pcc = ppc_cpu_class_by_pvr_mask(host_pvr);
+ }
+ if (pvr_pcc == NULL) {
return -1;
}
type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc));
@@ -1902,3 +1906,31 @@ int kvm_arch_on_sigbus(int code, void *addr)
void kvm_arch_init_irq_routing(KVMState *s)
{
}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+}
+
+void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
+{
+}
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 66c777174c..c5c1108e92 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -51,6 +51,7 @@ static char cpu_reg_names[10*3 + 22*4 /* GPR */
#endif
+ 10*4 + 22*5 /* FPR */
+ 2*(10*6 + 22*7) /* AVRh, AVRl */
+ + 10*5 + 22*6 /* VSR */
+ 8*5 /* CRF */];
static TCGv cpu_gpr[32];
#if !defined(TARGET_PPC64)
@@ -58,6 +59,7 @@ static TCGv cpu_gprh[32];
#endif
static TCGv_i64 cpu_fpr[32];
static TCGv_i64 cpu_avrh[32], cpu_avrl[32];
+static TCGv_i64 cpu_vsr[32];
static TCGv_i32 cpu_crf[8];
static TCGv cpu_nip;
static TCGv cpu_msr;
@@ -137,6 +139,11 @@ void ppc_translate_init(void)
#endif
p += (i < 10) ? 6 : 7;
cpu_reg_names_size -= (i < 10) ? 6 : 7;
+ snprintf(p, cpu_reg_names_size, "vsr%d", i);
+ cpu_vsr[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUPPCState, vsr[i]), p);
+ p += (i < 10) ? 5 : 6;
+ cpu_reg_names_size -= (i < 10) ? 5 : 6;
}
cpu_nip = tcg_global_mem_new(TCG_AREG0,
@@ -195,6 +202,7 @@ typedef struct DisasContext {
#endif
int fpu_enabled;
int altivec_enabled;
+ int vsx_enabled;
int spe_enabled;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
@@ -365,6 +373,12 @@ static inline int32_t name(uint32_t opcode) \
return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \
}
+#define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2) \
+static inline uint32_t name(uint32_t opcode) \
+{ \
+ return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \
+ ((opcode >> (shift2)) & ((1 << (nb2)) - 1)); \
+}
/* Opcode part 1 */
EXTRACT_HELPER(opc1, 26, 6);
/* Opcode part 2 */
@@ -479,6 +493,14 @@ static inline target_ulong MASK(uint32_t start, uint32_t end)
return ret;
}
+EXTRACT_HELPER_SPLIT(xT, 0, 1, 21, 5);
+EXTRACT_HELPER_SPLIT(xS, 0, 1, 21, 5);
+EXTRACT_HELPER_SPLIT(xA, 2, 1, 16, 5);
+EXTRACT_HELPER_SPLIT(xB, 1, 1, 11, 5);
+EXTRACT_HELPER_SPLIT(xC, 3, 1, 6, 5);
+EXTRACT_HELPER(DM, 8, 2);
+EXTRACT_HELPER(UIM, 16, 2);
+EXTRACT_HELPER(SHW, 8, 2);
/*****************************************************************************/
/* PowerPC instructions table */
@@ -2545,6 +2567,14 @@ static inline void gen_qemu_ld32u(DisasContext *ctx, TCGv arg1, TCGv arg2)
}
}
+static void gen_qemu_ld32u_i64(DisasContext *ctx, TCGv_i64 val, TCGv addr)
+{
+ TCGv tmp = tcg_temp_new();
+ gen_qemu_ld32u(ctx, tmp, addr);
+ tcg_gen_extu_tl_i64(val, tmp);
+ tcg_temp_free(tmp);
+}
+
static inline void gen_qemu_ld32s(DisasContext *ctx, TCGv arg1, TCGv arg2)
{
if (unlikely(ctx->le_mode)) {
@@ -2594,6 +2624,14 @@ static inline void gen_qemu_st32(DisasContext *ctx, TCGv arg1, TCGv arg2)
}
}
+static void gen_qemu_st32_i64(DisasContext *ctx, TCGv_i64 val, TCGv addr)
+{
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_trunc_i64_tl(tmp, val);
+ gen_qemu_st32(ctx, tmp, addr);
+ tcg_temp_free(tmp);
+}
+
static inline void gen_qemu_st64(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
{
if (unlikely(ctx->le_mode)) {
@@ -6964,10 +7002,476 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20)
GEN_VAFORM_PAIRED(vsel, vperm, 21)
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
+/*** VSX extension ***/
+
+static inline TCGv_i64 cpu_vsrh(int n)
+{
+ if (n < 32) {
+ return cpu_fpr[n];
+ } else {
+ return cpu_avrh[n-32];
+ }
+}
+
+static inline TCGv_i64 cpu_vsrl(int n)
+{
+ if (n < 32) {
+ return cpu_vsr[n];
+ } else {
+ return cpu_avrl[n-32];
+ }
+}
+
+static void gen_lxsdx(DisasContext *ctx)
+{
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_ld64(ctx, cpu_vsrh(xT(ctx->opcode)), EA);
+ /* NOTE: cpu_vsrl is undefined */
+ tcg_temp_free(EA);
+}
+
+static void gen_lxvd2x(DisasContext *ctx)
+{
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_ld64(ctx, cpu_vsrh(xT(ctx->opcode)), EA);
+ tcg_gen_addi_tl(EA, EA, 8);
+ gen_qemu_ld64(ctx, cpu_vsrl(xT(ctx->opcode)), EA);
+ tcg_temp_free(EA);
+}
+
+static void gen_lxvdsx(DisasContext *ctx)
+{
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_ld64(ctx, cpu_vsrh(xT(ctx->opcode)), EA);
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode)));
+ tcg_temp_free(EA);
+}
+
+static void gen_lxvw4x(DisasContext *ctx)
+{
+ TCGv EA;
+ TCGv_i64 tmp;
+ TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+ TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ tmp = tcg_temp_new_i64();
+
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_ld32u_i64(ctx, tmp, EA);
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_ld32u_i64(ctx, xth, EA);
+ tcg_gen_deposit_i64(xth, xth, tmp, 32, 32);
+
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_ld32u_i64(ctx, tmp, EA);
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_ld32u_i64(ctx, xtl, EA);
+ tcg_gen_deposit_i64(xtl, xtl, tmp, 32, 32);
+
+ tcg_temp_free(EA);
+ tcg_temp_free_i64(tmp);
+}
+
+static void gen_stxsdx(DisasContext *ctx)
+{
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_st64(ctx, cpu_vsrh(xS(ctx->opcode)), EA);
+ tcg_temp_free(EA);
+}
+
+static void gen_stxvd2x(DisasContext *ctx)
+{
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ gen_qemu_st64(ctx, cpu_vsrh(xS(ctx->opcode)), EA);
+ tcg_gen_addi_tl(EA, EA, 8);
+ gen_qemu_st64(ctx, cpu_vsrl(xS(ctx->opcode)), EA);
+ tcg_temp_free(EA);
+}
+
+static void gen_stxvw4x(DisasContext *ctx)
+{
+ TCGv_i64 tmp;
+ TCGv EA;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(tmp, cpu_vsrh(xS(ctx->opcode)), 32);
+ gen_qemu_st32_i64(ctx, tmp, EA);
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_st32_i64(ctx, cpu_vsrh(xS(ctx->opcode)), EA);
+
+ tcg_gen_shri_i64(tmp, cpu_vsrl(xS(ctx->opcode)), 32);
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_st32_i64(ctx, tmp, EA);
+ tcg_gen_addi_tl(EA, EA, 4);
+ gen_qemu_st32_i64(ctx, cpu_vsrl(xS(ctx->opcode)), EA);
+
+ tcg_temp_free(EA);
+ tcg_temp_free_i64(tmp);
+}
+
+static void gen_xxpermdi(DisasContext *ctx)
+{
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+
+ if ((DM(ctx->opcode) & 2) == 0) {
+ tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode)));
+ } else {
+ tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode)));
+ }
+ if ((DM(ctx->opcode) & 1) == 0) {
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode)));
+ } else {
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode)));
+ }
+}
+
+#define OP_ABS 1
+#define OP_NABS 2
+#define OP_NEG 3
+#define OP_CPSGN 4
+#define SGN_MASK_DP 0x8000000000000000ul
+#define SGN_MASK_SP 0x8000000080000000ul
+
+#define VSX_SCALAR_MOVE(name, op, sgn_mask) \
+static void glue(gen_, name)(DisasContext * ctx) \
+ { \
+ TCGv_i64 xb, sgm; \
+ if (unlikely(!ctx->vsx_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VSXU); \
+ return; \
+ } \
+ xb = tcg_temp_new_i64(); \
+ sgm = tcg_temp_new_i64(); \
+ tcg_gen_mov_i64(xb, cpu_vsrh(xB(ctx->opcode))); \
+ tcg_gen_movi_i64(sgm, sgn_mask); \
+ switch (op) { \
+ case OP_ABS: { \
+ tcg_gen_andc_i64(xb, xb, sgm); \
+ break; \
+ } \
+ case OP_NABS: { \
+ tcg_gen_or_i64(xb, xb, sgm); \
+ break; \
+ } \
+ case OP_NEG: { \
+ tcg_gen_xor_i64(xb, xb, sgm); \
+ break; \
+ } \
+ case OP_CPSGN: { \
+ TCGv_i64 xa = tcg_temp_new_i64(); \
+ tcg_gen_mov_i64(xa, cpu_vsrh(xA(ctx->opcode))); \
+ tcg_gen_and_i64(xa, xa, sgm); \
+ tcg_gen_andc_i64(xb, xb, sgm); \
+ tcg_gen_or_i64(xb, xb, xa); \
+ tcg_temp_free_i64(xa); \
+ break; \
+ } \
+ } \
+ tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xb); \
+ tcg_temp_free_i64(xb); \
+ tcg_temp_free_i64(sgm); \
+ }
+
+VSX_SCALAR_MOVE(xsabsdp, OP_ABS, SGN_MASK_DP)
+VSX_SCALAR_MOVE(xsnabsdp, OP_NABS, SGN_MASK_DP)
+VSX_SCALAR_MOVE(xsnegdp, OP_NEG, SGN_MASK_DP)
+VSX_SCALAR_MOVE(xscpsgndp, OP_CPSGN, SGN_MASK_DP)
+
+#define VSX_VECTOR_MOVE(name, op, sgn_mask) \
+static void glue(gen_, name)(DisasContext * ctx) \
+ { \
+ TCGv_i64 xbh, xbl, sgm; \
+ if (unlikely(!ctx->vsx_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VSXU); \
+ return; \
+ } \
+ xbh = tcg_temp_new_i64(); \
+ xbl = tcg_temp_new_i64(); \
+ sgm = tcg_temp_new_i64(); \
+ tcg_gen_mov_i64(xbh, cpu_vsrh(xB(ctx->opcode))); \
+ tcg_gen_mov_i64(xbl, cpu_vsrl(xB(ctx->opcode))); \
+ tcg_gen_movi_i64(sgm, sgn_mask); \
+ switch (op) { \
+ case OP_ABS: { \
+ tcg_gen_andc_i64(xbh, xbh, sgm); \
+ tcg_gen_andc_i64(xbl, xbl, sgm); \
+ break; \
+ } \
+ case OP_NABS: { \
+ tcg_gen_or_i64(xbh, xbh, sgm); \
+ tcg_gen_or_i64(xbl, xbl, sgm); \
+ break; \
+ } \
+ case OP_NEG: { \
+ tcg_gen_xor_i64(xbh, xbh, sgm); \
+ tcg_gen_xor_i64(xbl, xbl, sgm); \
+ break; \
+ } \
+ case OP_CPSGN: { \
+ TCGv_i64 xah = tcg_temp_new_i64(); \
+ TCGv_i64 xal = tcg_temp_new_i64(); \
+ tcg_gen_mov_i64(xah, cpu_vsrh(xA(ctx->opcode))); \
+ tcg_gen_mov_i64(xal, cpu_vsrl(xA(ctx->opcode))); \
+ tcg_gen_and_i64(xah, xah, sgm); \
+ tcg_gen_and_i64(xal, xal, sgm); \
+ tcg_gen_andc_i64(xbh, xbh, sgm); \
+ tcg_gen_andc_i64(xbl, xbl, sgm); \
+ tcg_gen_or_i64(xbh, xbh, xah); \
+ tcg_gen_or_i64(xbl, xbl, xal); \
+ tcg_temp_free_i64(xah); \
+ tcg_temp_free_i64(xal); \
+ break; \
+ } \
+ } \
+ tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xbh); \
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xbl); \
+ tcg_temp_free_i64(xbh); \
+ tcg_temp_free_i64(xbl); \
+ tcg_temp_free_i64(sgm); \
+ }
+
+VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP)
+VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP)
+VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP)
+VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP)
+VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP)
+VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP)
+VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP)
+VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP)
+
+
+#define VSX_LOGICAL(name, tcg_op) \
+static void glue(gen_, name)(DisasContext * ctx) \
+ { \
+ if (unlikely(!ctx->vsx_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VSXU); \
+ return; \
+ } \
+ tcg_op(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode)), \
+ cpu_vsrh(xB(ctx->opcode))); \
+ tcg_op(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode)), \
+ cpu_vsrl(xB(ctx->opcode))); \
+ }
+
+VSX_LOGICAL(xxland, tcg_gen_and_i64)
+VSX_LOGICAL(xxlandc, tcg_gen_andc_i64)
+VSX_LOGICAL(xxlor, tcg_gen_or_i64)
+VSX_LOGICAL(xxlxor, tcg_gen_xor_i64)
+VSX_LOGICAL(xxlnor, tcg_gen_nor_i64)
+
+#define VSX_XXMRG(name, high) \
+static void glue(gen_, name)(DisasContext * ctx) \
+ { \
+ TCGv_i64 a0, a1, b0, b1; \
+ if (unlikely(!ctx->vsx_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VSXU); \
+ return; \
+ } \
+ a0 = tcg_temp_new_i64(); \
+ a1 = tcg_temp_new_i64(); \
+ b0 = tcg_temp_new_i64(); \
+ b1 = tcg_temp_new_i64(); \
+ if (high) { \
+ tcg_gen_mov_i64(a0, cpu_vsrh(xA(ctx->opcode))); \
+ tcg_gen_mov_i64(a1, cpu_vsrh(xA(ctx->opcode))); \
+ tcg_gen_mov_i64(b0, cpu_vsrh(xB(ctx->opcode))); \
+ tcg_gen_mov_i64(b1, cpu_vsrh(xB(ctx->opcode))); \
+ } else { \
+ tcg_gen_mov_i64(a0, cpu_vsrl(xA(ctx->opcode))); \
+ tcg_gen_mov_i64(a1, cpu_vsrl(xA(ctx->opcode))); \
+ tcg_gen_mov_i64(b0, cpu_vsrl(xB(ctx->opcode))); \
+ tcg_gen_mov_i64(b1, cpu_vsrl(xB(ctx->opcode))); \
+ } \
+ tcg_gen_shri_i64(a0, a0, 32); \
+ tcg_gen_shri_i64(b0, b0, 32); \
+ tcg_gen_deposit_i64(cpu_vsrh(xT(ctx->opcode)), \
+ b0, a0, 32, 32); \
+ tcg_gen_deposit_i64(cpu_vsrl(xT(ctx->opcode)), \
+ b1, a1, 32, 32); \
+ tcg_temp_free_i64(a0); \
+ tcg_temp_free_i64(a1); \
+ tcg_temp_free_i64(b0); \
+ tcg_temp_free_i64(b1); \
+ }
+
+VSX_XXMRG(xxmrghw, 1)
+VSX_XXMRG(xxmrglw, 0)
+
+static void gen_xxsel(DisasContext * ctx)
+{
+ TCGv_i64 a, b, c;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ a = tcg_temp_new_i64();
+ b = tcg_temp_new_i64();
+ c = tcg_temp_new_i64();
+
+ tcg_gen_mov_i64(a, cpu_vsrh(xA(ctx->opcode)));
+ tcg_gen_mov_i64(b, cpu_vsrh(xB(ctx->opcode)));
+ tcg_gen_mov_i64(c, cpu_vsrh(xC(ctx->opcode)));
+
+ tcg_gen_and_i64(b, b, c);
+ tcg_gen_andc_i64(a, a, c);
+ tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), a, b);
+
+ tcg_gen_mov_i64(a, cpu_vsrl(xA(ctx->opcode)));
+ tcg_gen_mov_i64(b, cpu_vsrl(xB(ctx->opcode)));
+ tcg_gen_mov_i64(c, cpu_vsrl(xC(ctx->opcode)));
+
+ tcg_gen_and_i64(b, b, c);
+ tcg_gen_andc_i64(a, a, c);
+ tcg_gen_or_i64(cpu_vsrl(xT(ctx->opcode)), a, b);
+
+ tcg_temp_free_i64(a);
+ tcg_temp_free_i64(b);
+ tcg_temp_free_i64(c);
+}
+
+static void gen_xxspltw(DisasContext *ctx)
+{
+ TCGv_i64 b, b2;
+ TCGv_i64 vsr = (UIM(ctx->opcode) & 2) ?
+ cpu_vsrl(xB(ctx->opcode)) :
+ cpu_vsrh(xB(ctx->opcode));
+
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+
+ b = tcg_temp_new_i64();
+ b2 = tcg_temp_new_i64();
+
+ if (UIM(ctx->opcode) & 1) {
+ tcg_gen_ext32u_i64(b, vsr);
+ } else {
+ tcg_gen_shri_i64(b, vsr, 32);
+ }
+
+ tcg_gen_shli_i64(b2, b, 32);
+ tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), b, b2);
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode)));
+
+ tcg_temp_free_i64(b);
+ tcg_temp_free_i64(b2);
+}
+
+static void gen_xxsldwi(DisasContext *ctx)
+{
+ TCGv_i64 xth, xtl;
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ xth = tcg_temp_new_i64();
+ xtl = tcg_temp_new_i64();
+
+ switch (SHW(ctx->opcode)) {
+ case 0: {
+ tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode)));
+ tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode)));
+ break;
+ }
+ case 1: {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode)));
+ tcg_gen_shli_i64(xth, xth, 32);
+ tcg_gen_mov_i64(t0, cpu_vsrl(xA(ctx->opcode)));
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_or_i64(xth, xth, t0);
+ tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode)));
+ tcg_gen_shli_i64(xtl, xtl, 32);
+ tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode)));
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_or_i64(xtl, xtl, t0);
+ tcg_temp_free_i64(t0);
+ break;
+ }
+ case 2: {
+ tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode)));
+ tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode)));
+ break;
+ }
+ case 3: {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode)));
+ tcg_gen_shli_i64(xth, xth, 32);
+ tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode)));
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_or_i64(xth, xth, t0);
+ tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode)));
+ tcg_gen_shli_i64(xtl, xtl, 32);
+ tcg_gen_mov_i64(t0, cpu_vsrl(xB(ctx->opcode)));
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_or_i64(xtl, xtl, t0);
+ tcg_temp_free_i64(t0);
+ break;
+ }
+ }
+
+ tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xth);
+ tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xtl);
+
+ tcg_temp_free_i64(xth);
+ tcg_temp_free_i64(xtl);
+}
+
+
/*** SPE extension ***/
/* Register moves */
-
static inline void gen_evmra(DisasContext *ctx)
{
@@ -9413,6 +9917,119 @@ GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20),
GEN_VAFORM_PAIRED(vsel, vperm, 21),
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23),
+GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX),
+GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX),
+GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX),
+GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX),
+
+GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX),
+GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX),
+GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX),
+
+#undef GEN_XX2FORM
+#define GEN_XX2FORM(name, opc2, opc3, fl2) \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2)
+
+#undef GEN_XX3FORM
+#define GEN_XX3FORM(name, opc2, opc3, fl2) \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 0, PPC_NONE, fl2), \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 0, PPC_NONE, fl2)
+
+#undef GEN_XX3FORM_DM
+#define GEN_XX3FORM_DM(name, opc2, opc3) \
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x04, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x08, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x02, opc3|0x0C, 0, PPC_NONE, PPC2_VSX),\
+GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x03, opc3|0x0C, 0, PPC_NONE, PPC2_VSX)
+
+GEN_XX2FORM(xsabsdp, 0x12, 0x15, PPC2_VSX),
+GEN_XX2FORM(xsnabsdp, 0x12, 0x16, PPC2_VSX),
+GEN_XX2FORM(xsnegdp, 0x12, 0x17, PPC2_VSX),
+GEN_XX3FORM(xscpsgndp, 0x00, 0x16, PPC2_VSX),
+
+GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX),
+GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX),
+GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX),
+GEN_XX3FORM(xvcpsgndp, 0x00, 0x1E, PPC2_VSX),
+GEN_XX2FORM(xvabssp, 0x12, 0x19, PPC2_VSX),
+GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX),
+GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX),
+GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX),
+
+#undef VSX_LOGICAL
+#define VSX_LOGICAL(name, opc2, opc3, fl2) \
+GEN_XX3FORM(name, opc2, opc3, fl2)
+
+VSX_LOGICAL(xxland, 0x8, 0x10, PPC2_VSX),
+VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX),
+VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX),
+VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX),
+VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX),
+GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX),
+GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX),
+GEN_XX2FORM(xxspltw, 0x08, 0x0A, PPC2_VSX),
+GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00),
+
+#define GEN_XXSEL_ROW(opc3) \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x18, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x19, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1A, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1B, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1C, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1D, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1E, opc3, 0, PPC_NONE, PPC2_VSX), \
+GEN_HANDLER2_E(xxsel, "xxsel", 0x3C, 0x1F, opc3, 0, PPC_NONE, PPC2_VSX), \
+
+GEN_XXSEL_ROW(0x00)
+GEN_XXSEL_ROW(0x01)
+GEN_XXSEL_ROW(0x02)
+GEN_XXSEL_ROW(0x03)
+GEN_XXSEL_ROW(0x04)
+GEN_XXSEL_ROW(0x05)
+GEN_XXSEL_ROW(0x06)
+GEN_XXSEL_ROW(0x07)
+GEN_XXSEL_ROW(0x08)
+GEN_XXSEL_ROW(0x09)
+GEN_XXSEL_ROW(0x0A)
+GEN_XXSEL_ROW(0x0B)
+GEN_XXSEL_ROW(0x0C)
+GEN_XXSEL_ROW(0x0D)
+GEN_XXSEL_ROW(0x0E)
+GEN_XXSEL_ROW(0x0F)
+GEN_XXSEL_ROW(0x10)
+GEN_XXSEL_ROW(0x11)
+GEN_XXSEL_ROW(0x12)
+GEN_XXSEL_ROW(0x13)
+GEN_XXSEL_ROW(0x14)
+GEN_XXSEL_ROW(0x15)
+GEN_XXSEL_ROW(0x16)
+GEN_XXSEL_ROW(0x17)
+GEN_XXSEL_ROW(0x18)
+GEN_XXSEL_ROW(0x19)
+GEN_XXSEL_ROW(0x1A)
+GEN_XXSEL_ROW(0x1B)
+GEN_XXSEL_ROW(0x1C)
+GEN_XXSEL_ROW(0x1D)
+GEN_XXSEL_ROW(0x1E)
+GEN_XXSEL_ROW(0x1F)
+
+GEN_XX3FORM_DM(xxpermdi, 0x08, 0x01),
+
#undef GEN_SPE
#define GEN_SPE(name0, name1, opc2, opc3, inval0, inval1, type) \
GEN_OPCODE_DUAL(name0##_##name1, 0x04, opc2, opc3, inval0, inval1, type, PPC_NONE)
@@ -9759,6 +10376,11 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu,
ctx.altivec_enabled = msr_vr;
else
ctx.altivec_enabled = 0;
+ if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
+ ctx.vsx_enabled = msr_vsx;
+ } else {
+ ctx.vsx_enabled = 0;
+ }
if ((env->flags & POWERPC_FLAG_SE) && msr_se)
ctx.singlestep_enabled = CPU_SINGLE_STEP;
else
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 47825ac543..c030a2032a 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -3061,6 +3061,7 @@ static void init_excp_POWER7 (CPUPPCState *env)
env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
+ env->excp_vectors[POWERPC_EXCP_VSXU] = 0x00000F40;
env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600;
env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700;
@@ -7221,6 +7222,46 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
+ pcc->pvr = CPU_POWERPC_POWER7_BASE;
+ pcc->pvr_mask = CPU_POWERPC_POWER7_MASK;
+ pcc->init_proc = init_proc_POWER7;
+ pcc->check_pow = check_pow_nocheck;
+ pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
+ PPC_FLOAT_STFIWX |
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
+ PPC_MEM_SYNC | PPC_MEM_EIEIO |
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
+ PPC_64B | PPC_ALTIVEC |
+ PPC_SEGMENT_64B | PPC_SLBI |
+ PPC_POPCNTB | PPC_POPCNTWD;
+ pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205;
+ pcc->msr_mask = 0x800000000284FF37ULL;
+ pcc->mmu_model = POWERPC_MMU_2_06;
+#if defined(CONFIG_SOFTMMU)
+ pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
+#endif
+ pcc->excp_model = POWERPC_EXCP_POWER7;
+ pcc->bus_model = PPC_FLAGS_INPUT_POWER7;
+ pcc->bfd_mach = bfd_mach_ppc64;
+ pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
+ POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
+ POWERPC_FLAG_VSX;
+ pcc->l1_dcache_size = 0x8000;
+ pcc->l1_icache_size = 0x8000;
+}
+
+POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+
+ dc->fw_name = "PowerPC,POWER7+";
+ dc->desc = "POWER7+";
+ pcc->pvr = CPU_POWERPC_POWER7P_BASE;
+ pcc->pvr_mask = CPU_POWERPC_POWER7P_MASK;
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
@@ -7244,7 +7285,8 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->bfd_mach = bfd_mach_ppc64;
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
- POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR;
+ POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
+ POWERPC_FLAG_VSX;
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
}
@@ -7256,6 +7298,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
+ pcc->pvr = CPU_POWERPC_POWER8_BASE;
+ pcc->pvr_mask = CPU_POWERPC_POWER8_MASK;
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
@@ -7269,7 +7313,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
PPC_SEGMENT_64B | PPC_SLBI |
PPC_POPCNTB | PPC_POPCNTWD;
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX;
- pcc->msr_mask = 0x800000000204FF36ULL;
+ pcc->msr_mask = 0x800000000284FF36ULL;
pcc->mmu_model = POWERPC_MMU_2_06;
#if defined(CONFIG_SOFTMMU)
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
@@ -7279,7 +7323,8 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
pcc->bfd_mach = bfd_mach_ppc64;
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
- POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR;
+ POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
+ POWERPC_FLAG_VSX;
pcc->l1_dcache_size = 0x8000;
pcc->l1_icache_size = 0x8000;
}
@@ -8188,6 +8233,44 @@ PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr)
return pcc;
}
+static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b)
+{
+ ObjectClass *oc = (ObjectClass *)a;
+ uint32_t pvr = *(uint32_t *)b;
+ PowerPCCPUClass *pcc = (PowerPCCPUClass *)a;
+ gint ret;
+
+ /* -cpu host does a PVR lookup during construction */
+ if (unlikely(strcmp(object_class_get_name(oc),
+ TYPE_HOST_POWERPC_CPU) == 0)) {
+ return -1;
+ }
+
+#if defined(TARGET_PPCEMB)
+ if (pcc->mmu_model != POWERPC_MMU_BOOKE) {
+ return -1;
+ }
+#endif
+ ret = (((pcc->pvr & pcc->pvr_mask) == (pvr & pcc->pvr_mask)) ? 0 : -1);
+
+ return ret;
+}
+
+PowerPCCPUClass *ppc_cpu_class_by_pvr_mask(uint32_t pvr)
+{
+ GSList *list, *item;
+ PowerPCCPUClass *pcc = NULL;
+
+ list = object_class_get_list(TYPE_POWERPC_CPU, true);
+ item = g_slist_find_custom(list, &pvr, ppc_cpu_compare_class_pvr_mask);
+ if (item != NULL) {
+ pcc = POWERPC_CPU_CLASS(item->data);
+ }
+ g_slist_free(list);
+
+ return pcc;
+}
+
static gint ppc_cpu_compare_class_name(gconstpointer a, gconstpointer b)
{
ObjectClass *oc = (ObjectClass *)a;
@@ -8559,6 +8642,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
pcc->parent_realize = dc->realize;
+ pcc->pvr = CPU_POWERPC_DEFAULT_MASK;
+ pcc->pvr_mask = CPU_POWERPC_DEFAULT_MASK;
dc->realize = ppc_cpu_realizefn;
dc->unrealize = ppc_cpu_unrealizefn;
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index a2c077bdcd..68b5ab7056 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -352,6 +352,9 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
return addr;
}
+/* Base/displacement are at the same locations. */
+#define decode_basedisp_rs decode_basedisp_s
+
void s390x_tod_timer(void *opaque);
void s390x_cpu_timer(void *opaque);
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
index 8d6363df4e..b8a6486f51 100644
--- a/target-s390x/ioinst.c
+++ b/target-s390x/ioinst.c
@@ -622,6 +622,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb)
break;
}
+ setcc(cpu, 0); /* Command execution complete */
out:
s390_cpu_physical_memory_unmap(env, req, map_size, 1);
}
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 02ac4ba995..f7b772668c 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -82,11 +82,6 @@
#define ICPT_CPU_STOP 0x28
#define ICPT_IO 0x40
-#define SIGP_RESTART 0x06
-#define SIGP_INITIAL_CPU_RESET 0x0b
-#define SIGP_STORE_STATUS_ADDR 0x0e
-#define SIGP_SET_ARCH 0x12
-
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
KVM_CAP_LAST_INFO
};
@@ -562,11 +557,19 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
handle_diag_308(&cpu->env, r1, r3);
}
-static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code)
+#define DIAG_KVM_CODE_MASK 0x000000000000ffff
+
+static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
{
int r = 0;
-
- switch (ipb_code) {
+ uint16_t func_code;
+
+ /*
+ * For any diagnose call we support, bits 48-63 of the resulting
+ * address specify the function code; the remainder is ignored.
+ */
+ func_code = decode_basedisp_rs(&cpu->env, ipb) & DIAG_KVM_CODE_MASK;
+ switch (func_code) {
case DIAG_IPL:
kvm_handle_diag_308(cpu, run);
break;
@@ -577,7 +580,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code)
sleep(10);
break;
default:
- DPRINTF("KVM: unknown DIAG: 0x%x\n", ipb_code);
+ DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code);
r = -1;
break;
}
@@ -585,20 +588,21 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code)
return r;
}
-int kvm_s390_cpu_restart(S390CPU *cpu)
+static int kvm_s390_cpu_start(S390CPU *cpu)
{
- kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0);
s390_add_running_cpu(cpu);
qemu_cpu_kick(CPU(cpu));
- DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
+ DPRINTF("DONE: KVM cpu start: %p\n", &cpu->env);
return 0;
}
-static int s390_store_status(CPUS390XState *env, uint32_t parameter)
+int kvm_s390_cpu_restart(S390CPU *cpu)
{
- /* XXX */
- fprintf(stderr, "XXX SIGP store status\n");
- return -1;
+ kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0);
+ s390_add_running_cpu(cpu);
+ qemu_cpu_kick(CPU(cpu));
+ DPRINTF("DONE: KVM cpu restart: %p\n", &cpu->env);
+ return 0;
}
static int s390_cpu_initial_reset(S390CPU *cpu)
@@ -622,61 +626,52 @@ static int s390_cpu_initial_reset(S390CPU *cpu)
return 0;
}
+#define SIGP_ORDER_MASK 0x000000ff
+
static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
{
CPUS390XState *env = &cpu->env;
uint8_t order_code;
- uint32_t parameter;
uint16_t cpu_addr;
- uint8_t t;
- int r = -1;
S390CPU *target_cpu;
- CPUS390XState *target_env;
+ uint64_t *statusreg = &env->regs[ipa1 >> 4];
+ int cc;
cpu_synchronize_state(CPU(cpu));
/* get order code */
- order_code = run->s390_sieic.ipb >> 28;
- if (order_code > 0) {
- order_code = env->regs[order_code];
- }
- order_code += (run->s390_sieic.ipb & 0x0fff0000) >> 16;
+ order_code = decode_basedisp_rs(env, run->s390_sieic.ipb) & SIGP_ORDER_MASK;
- /* get parameters */
- t = (ipa1 & 0xf0) >> 4;
- if (!(t % 2)) {
- t++;
- }
-
- parameter = env->regs[t] & 0x7ffffe00;
cpu_addr = env->regs[ipa1 & 0x0f];
-
target_cpu = s390_cpu_addr2state(cpu_addr);
if (target_cpu == NULL) {
+ cc = 3; /* not operational */
goto out;
}
- target_env = &target_cpu->env;
switch (order_code) {
- case SIGP_RESTART:
- r = kvm_s390_cpu_restart(target_cpu);
- break;
- case SIGP_STORE_STATUS_ADDR:
- r = s390_store_status(target_env, parameter);
- break;
- case SIGP_SET_ARCH:
- /* make the caller panic */
- return -1;
- case SIGP_INITIAL_CPU_RESET:
- r = s390_cpu_initial_reset(target_cpu);
- break;
- default:
- fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", order_code);
- break;
+ case SIGP_START:
+ cc = kvm_s390_cpu_start(target_cpu);
+ break;
+ case SIGP_RESTART:
+ cc = kvm_s390_cpu_restart(target_cpu);
+ break;
+ case SIGP_SET_ARCH:
+ /* make the caller panic */
+ return -1;
+ case SIGP_INITIAL_CPU_RESET:
+ cc = s390_cpu_initial_reset(target_cpu);
+ break;
+ default:
+ DPRINTF("KVM: unknown SIGP: 0x%x\n", order_code);
+ *statusreg &= 0xffffffff00000000UL;
+ *statusreg |= SIGP_STAT_INVALID_ORDER;
+ cc = 1; /* status stored */
+ break;
}
out:
- setcc(cpu, r ? 3 : 0);
+ setcc(cpu, cc);
return 0;
}
@@ -684,7 +679,6 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
{
unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
- int ipb_code = (run->s390_sieic.ipb & 0x0fff0000) >> 16;
int r = -1;
DPRINTF("handle_instruction 0x%x 0x%x\n",
@@ -696,7 +690,7 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run)
r = handle_priv(cpu, run, ipa0 >> 8, ipa1);
break;
case IPA0_DIAG:
- r = handle_diag(cpu, run, ipb_code);
+ r = handle_diag(cpu, run, run->s390_sieic.ipb);
break;
case IPA0_SIGP:
r = handle_sigp(cpu, run, ipa1);
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
index 2272eb0beb..661fc6c887 100644
--- a/target-sh4/translate.c
+++ b/target-sh4/translate.c
@@ -464,7 +464,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4);
- tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
tcg_temp_free(addr);
}
return;
@@ -472,7 +472,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4);
- tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
tcg_temp_free(addr);
}
return;
@@ -482,14 +482,14 @@ static void _decode_opc(DisasContext * ctx)
case 0x9000: /* mov.w @(disp,PC),Rn */
{
TCGv addr = tcg_const_i32(ctx->pc + 4 + B7_0 * 2);
- tcg_gen_qemu_ld16s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW);
tcg_temp_free(addr);
}
return;
case 0xd000: /* mov.l @(disp,PC),Rn */
{
TCGv addr = tcg_const_i32((ctx->pc + 4 + B7_0 * 4) & ~3);
- tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
tcg_temp_free(addr);
}
return;
@@ -516,28 +516,29 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_mov_i32(REG(B11_8), REG(B7_4));
return;
case 0x2000: /* mov.b Rm,@Rn */
- tcg_gen_qemu_st8(REG(B7_4), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_UB);
return;
case 0x2001: /* mov.w Rm,@Rn */
- tcg_gen_qemu_st16(REG(B7_4), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUW);
return;
case 0x2002: /* mov.l Rm,@Rn */
- tcg_gen_qemu_st32(REG(B7_4), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL);
return;
case 0x6000: /* mov.b @Rm,Rn */
- tcg_gen_qemu_ld8s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB);
return;
case 0x6001: /* mov.w @Rm,Rn */
- tcg_gen_qemu_ld16s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW);
return;
case 0x6002: /* mov.l @Rm,Rn */
- tcg_gen_qemu_ld32s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL);
return;
case 0x2004: /* mov.b Rm,@-Rn */
{
TCGv addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 1);
- tcg_gen_qemu_st8(REG(B7_4), addr, ctx->memidx); /* might cause re-execution */
+ /* might cause re-execution */
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB);
tcg_gen_mov_i32(REG(B11_8), addr); /* modify register status */
tcg_temp_free(addr);
}
@@ -546,7 +547,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 2);
- tcg_gen_qemu_st16(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW);
tcg_gen_mov_i32(REG(B11_8), addr);
tcg_temp_free(addr);
}
@@ -555,22 +556,22 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr);
}
return;
case 0x6004: /* mov.b @Rm+,Rn */
- tcg_gen_qemu_ld8s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB);
if ( B11_8 != B7_4 )
tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1);
return;
case 0x6005: /* mov.w @Rm+,Rn */
- tcg_gen_qemu_ld16s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW);
if ( B11_8 != B7_4 )
tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2);
return;
case 0x6006: /* mov.l @Rm+,Rn */
- tcg_gen_qemu_ld32s(REG(B11_8), REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL);
if ( B11_8 != B7_4 )
tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
return;
@@ -578,7 +579,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B11_8), REG(0));
- tcg_gen_qemu_st8(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB);
tcg_temp_free(addr);
}
return;
@@ -586,7 +587,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B11_8), REG(0));
- tcg_gen_qemu_st16(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW);
tcg_temp_free(addr);
}
return;
@@ -594,7 +595,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B11_8), REG(0));
- tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
tcg_temp_free(addr);
}
return;
@@ -602,7 +603,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B7_4), REG(0));
- tcg_gen_qemu_ld8s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_SB);
tcg_temp_free(addr);
}
return;
@@ -610,7 +611,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B7_4), REG(0));
- tcg_gen_qemu_ld16s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW);
tcg_temp_free(addr);
}
return;
@@ -618,7 +619,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(B7_4), REG(0));
- tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
tcg_temp_free(addr);
}
return;
@@ -767,9 +768,9 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv arg0, arg1;
arg0 = tcg_temp_new();
- tcg_gen_qemu_ld32s(arg0, REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL);
arg1 = tcg_temp_new();
- tcg_gen_qemu_ld32s(arg1, REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL);
gen_helper_macl(cpu_env, arg0, arg1);
tcg_temp_free(arg1);
tcg_temp_free(arg0);
@@ -781,9 +782,9 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv arg0, arg1;
arg0 = tcg_temp_new();
- tcg_gen_qemu_ld32s(arg0, REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL);
arg1 = tcg_temp_new();
- tcg_gen_qemu_ld32s(arg1, REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL);
gen_helper_macw(cpu_env, arg0, arg1);
tcg_temp_free(arg1);
tcg_temp_free(arg0);
@@ -979,11 +980,14 @@ static void _decode_opc(DisasContext * ctx)
TCGv addr_hi = tcg_temp_new();
int fr = XREG(B7_4);
tcg_gen_addi_i32(addr_hi, REG(B11_8), 4);
- tcg_gen_qemu_st32(cpu_fregs[fr ], REG(B11_8), ctx->memidx);
- tcg_gen_qemu_st32(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_gen_qemu_st_i32(cpu_fregs[fr], REG(B11_8),
+ ctx->memidx, MO_TEUL);
+ tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr_hi,
+ ctx->memidx, MO_TEUL);
tcg_temp_free(addr_hi);
} else {
- tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], REG(B11_8),
+ ctx->memidx, MO_TEUL);
}
return;
case 0xf008: /* fmov @Rm,{F,D,X}Rn - FPSCR: Nothing */
@@ -992,11 +996,12 @@ static void _decode_opc(DisasContext * ctx)
TCGv addr_hi = tcg_temp_new();
int fr = XREG(B11_8);
tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
- tcg_gen_qemu_ld32u(cpu_fregs[fr ], REG(B7_4), ctx->memidx);
- tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL);
tcg_temp_free(addr_hi);
} else {
- tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4),
+ ctx->memidx, MO_TEUL);
}
return;
case 0xf009: /* fmov @Rm+,{F,D,X}Rn - FPSCR: Nothing */
@@ -1005,12 +1010,13 @@ static void _decode_opc(DisasContext * ctx)
TCGv addr_hi = tcg_temp_new();
int fr = XREG(B11_8);
tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
- tcg_gen_qemu_ld32u(cpu_fregs[fr ], REG(B7_4), ctx->memidx);
- tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL);
tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8);
tcg_temp_free(addr_hi);
} else {
- tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4),
+ ctx->memidx, MO_TEUL);
tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
}
return;
@@ -1020,16 +1026,17 @@ static void _decode_opc(DisasContext * ctx)
TCGv addr = tcg_temp_new_i32();
int fr = XREG(B7_4);
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st32(cpu_fregs[fr+1], addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL);
tcg_gen_subi_i32(addr, addr, 4);
- tcg_gen_qemu_st32(cpu_fregs[fr ], addr, ctx->memidx);
+ 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_st32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx);
+ 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);
}
@@ -1041,11 +1048,14 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_add_i32(addr, REG(B7_4), REG(0));
if (ctx->flags & FPSCR_SZ) {
int fr = XREG(B11_8);
- tcg_gen_qemu_ld32u(cpu_fregs[fr ], addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr,
+ ctx->memidx, MO_TEUL);
tcg_gen_addi_i32(addr, addr, 4);
- tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr,
+ ctx->memidx, MO_TEUL);
} else {
- tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], addr,
+ ctx->memidx, MO_TEUL);
}
tcg_temp_free(addr);
}
@@ -1057,11 +1067,14 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_add_i32(addr, REG(B11_8), REG(0));
if (ctx->flags & FPSCR_SZ) {
int fr = XREG(B7_4);
- tcg_gen_qemu_ld32u(cpu_fregs[fr ], addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr,
+ ctx->memidx, MO_TEUL);
tcg_gen_addi_i32(addr, addr, 4);
- tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr,
+ ctx->memidx, MO_TEUL);
} else {
- tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr,
+ ctx->memidx, MO_TEUL);
}
tcg_temp_free(addr);
}
@@ -1164,9 +1177,9 @@ static void _decode_opc(DisasContext * ctx)
addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(0), cpu_gbr);
val = tcg_temp_new();
- tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
tcg_gen_andi_i32(val, val, B7_0);
- tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
tcg_temp_free(val);
tcg_temp_free(addr);
}
@@ -1200,7 +1213,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
- tcg_gen_qemu_ld8s(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB);
tcg_temp_free(addr);
}
return;
@@ -1208,7 +1221,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
- tcg_gen_qemu_ld16s(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW);
tcg_temp_free(addr);
}
return;
@@ -1216,7 +1229,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
- tcg_gen_qemu_ld32s(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESL);
tcg_temp_free(addr);
}
return;
@@ -1224,7 +1237,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
- tcg_gen_qemu_st8(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB);
tcg_temp_free(addr);
}
return;
@@ -1232,7 +1245,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
- tcg_gen_qemu_st16(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW);
tcg_temp_free(addr);
}
return;
@@ -1240,7 +1253,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
- tcg_gen_qemu_st32(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUL);
tcg_temp_free(addr);
}
return;
@@ -1248,7 +1261,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
- tcg_gen_qemu_st8(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB);
tcg_temp_free(addr);
}
return;
@@ -1256,7 +1269,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
- tcg_gen_qemu_st16(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW);
tcg_temp_free(addr);
}
return;
@@ -1264,7 +1277,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
- tcg_gen_qemu_ld8s(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB);
tcg_temp_free(addr);
}
return;
@@ -1272,7 +1285,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
- tcg_gen_qemu_ld16s(REG(0), addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW);
tcg_temp_free(addr);
}
return;
@@ -1288,9 +1301,9 @@ static void _decode_opc(DisasContext * ctx)
addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(0), cpu_gbr);
val = tcg_temp_new();
- tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
tcg_gen_ori_i32(val, val, B7_0);
- tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
tcg_temp_free(val);
tcg_temp_free(addr);
}
@@ -1318,7 +1331,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv val = tcg_temp_new();
tcg_gen_add_i32(val, REG(0), cpu_gbr);
- tcg_gen_qemu_ld8u(val, val, ctx->memidx);
+ 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_temp_free(val);
@@ -1333,9 +1346,9 @@ static void _decode_opc(DisasContext * ctx)
addr = tcg_temp_new();
tcg_gen_add_i32(addr, REG(0), cpu_gbr);
val = tcg_temp_new();
- tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
tcg_gen_xori_i32(val, val, B7_0);
- tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
tcg_temp_free(val);
tcg_temp_free(addr);
}
@@ -1349,7 +1362,7 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x4087: /* ldc.l @Rm+,Rn_BANK */
CHECK_PRIVILEGED
- tcg_gen_qemu_ld32s(ALTREG(B6_4), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(ALTREG(B6_4), REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
return;
case 0x0082: /* stc Rm_BANK,Rn */
@@ -1361,7 +1374,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st32(ALTREG(B6_4), addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(ALTREG(B6_4), addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr);
tcg_temp_free(addr);
}
@@ -1414,7 +1427,7 @@ static void _decode_opc(DisasContext * ctx)
CHECK_PRIVILEGED
{
TCGv val = tcg_temp_new();
- tcg_gen_qemu_ld32s(val, REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_andi_i32(cpu_sr, val, 0x700083f3);
tcg_temp_free(val);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
@@ -1430,7 +1443,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st32(cpu_sr, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(cpu_sr, addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr);
tcg_temp_free(addr);
}
@@ -1442,7 +1455,7 @@ static void _decode_opc(DisasContext * ctx)
return; \
case ldpnum: \
prechk \
- tcg_gen_qemu_ld32s (cpu_##reg, REG(B11_8), ctx->memidx); \
+ tcg_gen_qemu_ld_i32(cpu_##reg, REG(B11_8), ctx->memidx, MO_TESL); \
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); \
return;
#define ST(reg,stnum,stpnum,prechk) \
@@ -1455,7 +1468,7 @@ static void _decode_opc(DisasContext * ctx)
{ \
TCGv addr = tcg_temp_new(); \
tcg_gen_subi_i32(addr, REG(B11_8), 4); \
- tcg_gen_qemu_st32 (cpu_##reg, addr, ctx->memidx); \
+ tcg_gen_qemu_st_i32(cpu_##reg, addr, ctx->memidx, MO_TEUL); \
tcg_gen_mov_i32(REG(B11_8), addr); \
tcg_temp_free(addr); \
} \
@@ -1483,7 +1496,7 @@ static void _decode_opc(DisasContext * ctx)
CHECK_FPU_ENABLED
{
TCGv addr = tcg_temp_new();
- tcg_gen_qemu_ld32s(addr, REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(addr, REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
gen_helper_ld_fpscr(cpu_env, addr);
tcg_temp_free(addr);
@@ -1502,7 +1515,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff);
addr = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st32(val, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr);
tcg_temp_free(addr);
tcg_temp_free(val);
@@ -1511,21 +1524,21 @@ static void _decode_opc(DisasContext * ctx)
case 0x00c3: /* movca.l R0,@Rm */
{
TCGv val = tcg_temp_new();
- tcg_gen_qemu_ld32u(val, REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL);
gen_helper_movcal(cpu_env, REG(B11_8), val);
- tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
}
ctx->has_movcal = 1;
return;
case 0x40a9:
/* MOVUA.L @Rm,R0 (Rm) -> R0
Load non-boundary-aligned data */
- tcg_gen_qemu_ld32u(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
return;
case 0x40e9:
/* MOVUA.L @Rm+,R0 (Rm) -> R0, Rm + 4 -> Rm
Load non-boundary-aligned data */
- tcg_gen_qemu_ld32u(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
return;
case 0x0029: /* movt Rn */
@@ -1542,7 +1555,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst);
tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
- tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
gen_set_label(label);
tcg_gen_movi_i32(cpu_ldst, 0);
return;
@@ -1557,7 +1570,7 @@ static void _decode_opc(DisasContext * ctx)
*/
if (ctx->features & SH_FEATURE_SH4A) {
tcg_gen_movi_i32(cpu_ldst, 0);
- tcg_gen_qemu_ld32s(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL);
tcg_gen_movi_i32(cpu_ldst, 1);
return;
} else
@@ -1655,10 +1668,10 @@ static void _decode_opc(DisasContext * ctx)
addr = tcg_temp_local_new();
tcg_gen_mov_i32(addr, REG(B11_8));
val = tcg_temp_local_new();
- tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
gen_cmp_imm(TCG_COND_EQ, val, 0);
tcg_gen_ori_i32(val, val, 0x80);
- tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
tcg_temp_free(val);
tcg_temp_free(addr);
}
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index dce64c3c4a..6150b22f8f 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -3626,6 +3626,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
if ((rd == 0x13) && (dc->def->features &
CPU_FEATURE_POWERDOWN)) {
/* LEON3 power-down */
+ save_state(dc);
gen_helper_power_down(cpu_env);
}
break;
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
index 7ac8e45485..495b901080 100644
--- a/tcg/i386/tcg-target.c
+++ b/tcg/i386/tcg-target.c
@@ -1486,7 +1486,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi,
switch (memop & MO_SIZE) {
case MO_8:
- /* In 32-bit mode, 8-byte stores can only happen from [abcd]x.
+ /* In 32-bit mode, 8-bit stores can only happen from [abcd]x.
Use the scratch register if necessary. */
if (TCG_TARGET_REG_BITS == 32 && datalo >= 4) {
tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
diff --git a/tests/.gitignore b/tests/.gitignore
index 425757cfe1..1aed2249ff 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -20,6 +20,7 @@ test-qmp-commands
test-qmp-input-strict
test-qmp-marshal.c
test-thread-pool
+test-vmstate
test-x86-cpuid
test-xbzrle
*-test
diff --git a/tests/Makefile b/tests/Makefile
index 379cdd9ad1..fd36eee641 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -52,6 +52,9 @@ check-unit-y += tests/test-int128$(EXESUF)
gcov-files-test-int128-y =
check-unit-y += tests/test-bitops$(EXESUF)
check-unit-y += tests/test-qdev-global-props$(EXESUF)
+check-unit-y += tests/check-qom-interface$(EXESUF)
+gcov-files-check-qom-interface-y = qom/object.c
+check-unit-y += tests/test-vmstate$(EXESUF)
check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -64,6 +67,7 @@ check-qtest-i386-y += tests/ide-test$(EXESUF)
check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
gcov-files-i386-y += hw/hd-geometry.c
check-qtest-i386-y += tests/boot-order-test$(EXESUF)
+check-qtest-i386-y += tests/acpi-test$(EXESUF)
check-qtest-i386-y += tests/rtc-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
@@ -137,6 +141,7 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o
$(test-obj-y): QEMU_INCLUDES += -Itests
QEMU_CFLAGS += -I$(SRC_PATH)/tests
+qom-core-obj = qom/object.o qom/qom-qobject.o qom/container.o
tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386
@@ -146,6 +151,7 @@ tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a
tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a
tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a
tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
+tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(qom-core-obj) libqemuutil.a libqemustub.a
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a
@@ -159,9 +165,12 @@ tests/test-int128$(EXESUF): tests/test-int128.o
tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
hw/core/qdev.o hw/core/qdev-properties.o \
hw/core/irq.o \
- qom/object.o qom/container.o qom/qom-qobject.o \
+ $(qom-core-obj) \
$(test-qapi-obj-y) \
libqemuutil.a libqemustub.a
+tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
+ vmstate.o qemu-file.o \
+ libqemuutil.a
tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
@@ -198,6 +207,7 @@ tests/fdc-test$(EXESUF): tests/fdc-test.o
tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
+tests/acpi-test$(EXESUF): tests/acpi-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/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
diff --git a/tests/acpi-test.c b/tests/acpi-test.c
new file mode 100644
index 0000000000..df1af83158
--- /dev/null
+++ b/tests/acpi-test.c
@@ -0,0 +1,397 @@
+/*
+ * Boot order test cases.
+ *
+ * Copyright (c) 2013 Red Hat Inc.
+ *
+ * Authors:
+ * Michael S. Tsirkin <mst@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 <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include "qemu-common.h"
+#include "libqtest.h"
+#include "qemu/compiler.h"
+#include "hw/i386/acpi-defs.h"
+
+/* DSDT and SSDTs format */
+typedef struct {
+ AcpiTableHeader header;
+ uint8_t *aml;
+ int aml_len;
+} AcpiSdtTable;
+
+typedef struct {
+ uint32_t rsdp_addr;
+ AcpiRsdpDescriptor rsdp_table;
+ AcpiRsdtDescriptorRev1 rsdt_table;
+ AcpiFadtDescriptorRev1 fadt_table;
+ AcpiFacsDescriptorRev1 facs_table;
+ uint32_t *rsdt_tables_addr;
+ int rsdt_tables_nr;
+ AcpiSdtTable dsdt_table;
+ GArray *ssdt_tables;
+} test_data;
+
+#define LOW(x) ((x) & 0xff)
+#define HIGH(x) ((x) >> 8)
+
+#define SIGNATURE 0xdead
+#define SIGNATURE_OFFSET 0x10
+#define BOOT_SECTOR_ADDRESS 0x7c00
+
+#define ACPI_READ_FIELD(field, addr) \
+ do { \
+ switch (sizeof(field)) { \
+ case 1: \
+ field = readb(addr); \
+ break; \
+ case 2: \
+ field = le16_to_cpu(readw(addr)); \
+ break; \
+ case 4: \
+ field = le32_to_cpu(readl(addr)); \
+ break; \
+ case 8: \
+ field = le64_to_cpu(readq(addr)); \
+ break; \
+ default: \
+ g_assert(false); \
+ } \
+ addr += sizeof(field); \
+ } while (0);
+
+#define ACPI_READ_ARRAY_PTR(arr, length, addr) \
+ do { \
+ int idx; \
+ for (idx = 0; idx < length; ++idx) { \
+ ACPI_READ_FIELD(arr[idx], addr); \
+ } \
+ } while (0);
+
+#define ACPI_READ_ARRAY(arr, addr) \
+ ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr)
+
+#define ACPI_READ_TABLE_HEADER(table, addr) \
+ do { \
+ ACPI_READ_FIELD((table)->signature, addr); \
+ ACPI_READ_FIELD((table)->length, addr); \
+ ACPI_READ_FIELD((table)->revision, addr); \
+ ACPI_READ_FIELD((table)->checksum, addr); \
+ ACPI_READ_ARRAY((table)->oem_id, addr); \
+ ACPI_READ_ARRAY((table)->oem_table_id, addr); \
+ ACPI_READ_FIELD((table)->oem_revision, addr); \
+ ACPI_READ_ARRAY((table)->asl_compiler_id, addr); \
+ ACPI_READ_FIELD((table)->asl_compiler_revision, addr); \
+ } while (0);
+
+/* Boot sector code: write SIGNATURE into memory,
+ * then halt.
+ */
+static uint8_t boot_sector[0x200] = {
+ /* 7c00: mov $0xdead,%ax */
+ [0x00] = 0xb8,
+ [0x01] = LOW(SIGNATURE),
+ [0x02] = HIGH(SIGNATURE),
+ /* 7c03: mov %ax,0x7c10 */
+ [0x03] = 0xa3,
+ [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
+ [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
+ /* 7c06: cli */
+ [0x06] = 0xfa,
+ /* 7c07: hlt */
+ [0x07] = 0xf4,
+ /* 7c08: jmp 0x7c07=0x7c0a-3 */
+ [0x08] = 0xeb,
+ [0x09] = LOW(-3),
+ /* We mov 0xdead here: set value to make debugging easier */
+ [SIGNATURE_OFFSET] = LOW(0xface),
+ [SIGNATURE_OFFSET + 1] = HIGH(0xface),
+ /* End of boot sector marker */
+ [0x1FE] = 0x55,
+ [0x1FF] = 0xAA,
+};
+
+static const char *disk = "tests/acpi-test-disk.raw";
+
+static void free_test_data(test_data *data)
+{
+ int i;
+
+ g_free(data->rsdt_tables_addr);
+ for (i = 0; i < data->ssdt_tables->len; ++i) {
+ g_free(g_array_index(data->ssdt_tables, AcpiSdtTable, i).aml);
+ }
+ g_array_free(data->ssdt_tables, false);
+ g_free(data->dsdt_table.aml);
+}
+
+static uint8_t acpi_checksum(const uint8_t *data, int len)
+{
+ int i;
+ uint8_t sum = 0;
+
+ for (i = 0; i < len; i++) {
+ sum += data[i];
+ }
+
+ return sum;
+}
+
+static void test_acpi_rsdp_address(test_data *data)
+{
+ uint32_t off;
+
+ /* OK, now find RSDP */
+ for (off = 0xf0000; off < 0x100000; off += 0x10) {
+ uint8_t sig[] = "RSD PTR ";
+ int i;
+
+ for (i = 0; i < sizeof sig - 1; ++i) {
+ sig[i] = readb(off + i);
+ }
+
+ if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
+ break;
+ }
+ }
+
+ g_assert_cmphex(off, <, 0x100000);
+ data->rsdp_addr = off;
+}
+
+static void test_acpi_rsdp_table(test_data *data)
+{
+ AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table;
+ uint32_t addr = data->rsdp_addr;
+
+ ACPI_READ_FIELD(rsdp_table->signature, addr);
+ g_assert_cmphex(rsdp_table->signature, ==, ACPI_RSDP_SIGNATURE);
+
+ ACPI_READ_FIELD(rsdp_table->checksum, addr);
+ ACPI_READ_ARRAY(rsdp_table->oem_id, addr);
+ ACPI_READ_FIELD(rsdp_table->revision, addr);
+ ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr);
+ ACPI_READ_FIELD(rsdp_table->length, addr);
+
+ /* rsdp checksum is not for the whole table, but for the first 20 bytes */
+ g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20));
+}
+
+static void test_acpi_rsdt_table(test_data *data)
+{
+ AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table;
+ uint32_t addr = data->rsdp_table.rsdt_physical_address;
+ uint32_t *tables;
+ int tables_nr;
+ uint8_t checksum;
+
+ /* read the header */
+ ACPI_READ_TABLE_HEADER(rsdt_table, addr);
+ g_assert_cmphex(rsdt_table->signature, ==, ACPI_RSDT_SIGNATURE);
+
+ /* compute the table entries in rsdt */
+ tables_nr = (rsdt_table->length - sizeof(AcpiRsdtDescriptorRev1)) /
+ sizeof(uint32_t);
+ g_assert_cmpint(tables_nr, >, 0);
+
+ /* get the addresses of the tables pointed by rsdt */
+ tables = g_new0(uint32_t, tables_nr);
+ ACPI_READ_ARRAY_PTR(tables, tables_nr, addr);
+
+ checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) +
+ acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t));
+ g_assert(!checksum);
+
+ /* SSDT tables after FADT */
+ data->rsdt_tables_addr = tables;
+ data->rsdt_tables_nr = tables_nr;
+}
+
+static void test_acpi_fadt_table(test_data *data)
+{
+ AcpiFadtDescriptorRev1 *fadt_table = &data->fadt_table;
+ uint32_t addr;
+
+ /* FADT table comes first */
+ addr = data->rsdt_tables_addr[0];
+ ACPI_READ_TABLE_HEADER(fadt_table, addr);
+
+ ACPI_READ_FIELD(fadt_table->firmware_ctrl, addr);
+ ACPI_READ_FIELD(fadt_table->dsdt, addr);
+ ACPI_READ_FIELD(fadt_table->model, addr);
+ ACPI_READ_FIELD(fadt_table->reserved1, addr);
+ ACPI_READ_FIELD(fadt_table->sci_int, addr);
+ ACPI_READ_FIELD(fadt_table->smi_cmd, addr);
+ ACPI_READ_FIELD(fadt_table->acpi_enable, addr);
+ ACPI_READ_FIELD(fadt_table->acpi_disable, addr);
+ ACPI_READ_FIELD(fadt_table->S4bios_req, addr);
+ ACPI_READ_FIELD(fadt_table->reserved2, addr);
+ ACPI_READ_FIELD(fadt_table->pm1a_evt_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm1b_evt_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm1a_cnt_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm1b_cnt_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm2_cnt_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm_tmr_blk, addr);
+ ACPI_READ_FIELD(fadt_table->gpe0_blk, addr);
+ ACPI_READ_FIELD(fadt_table->gpe1_blk, addr);
+ ACPI_READ_FIELD(fadt_table->pm1_evt_len, addr);
+ ACPI_READ_FIELD(fadt_table->pm1_cnt_len, addr);
+ ACPI_READ_FIELD(fadt_table->pm2_cnt_len, addr);
+ ACPI_READ_FIELD(fadt_table->pm_tmr_len, addr);
+ ACPI_READ_FIELD(fadt_table->gpe0_blk_len, addr);
+ ACPI_READ_FIELD(fadt_table->gpe1_blk_len, addr);
+ ACPI_READ_FIELD(fadt_table->gpe1_base, addr);
+ ACPI_READ_FIELD(fadt_table->reserved3, addr);
+ ACPI_READ_FIELD(fadt_table->plvl2_lat, addr);
+ ACPI_READ_FIELD(fadt_table->plvl3_lat, addr);
+ ACPI_READ_FIELD(fadt_table->flush_size, addr);
+ ACPI_READ_FIELD(fadt_table->flush_stride, addr);
+ ACPI_READ_FIELD(fadt_table->duty_offset, addr);
+ ACPI_READ_FIELD(fadt_table->duty_width, addr);
+ ACPI_READ_FIELD(fadt_table->day_alrm, addr);
+ ACPI_READ_FIELD(fadt_table->mon_alrm, addr);
+ ACPI_READ_FIELD(fadt_table->century, addr);
+ ACPI_READ_FIELD(fadt_table->reserved4, addr);
+ ACPI_READ_FIELD(fadt_table->reserved4a, addr);
+ ACPI_READ_FIELD(fadt_table->reserved4b, addr);
+ ACPI_READ_FIELD(fadt_table->flags, addr);
+
+ g_assert_cmphex(fadt_table->signature, ==, ACPI_FACP_SIGNATURE);
+ g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length));
+}
+
+static void test_acpi_facs_table(test_data *data)
+{
+ AcpiFacsDescriptorRev1 *facs_table = &data->facs_table;
+ uint32_t addr = data->fadt_table.firmware_ctrl;
+
+ ACPI_READ_FIELD(facs_table->signature, addr);
+ ACPI_READ_FIELD(facs_table->length, addr);
+ ACPI_READ_FIELD(facs_table->hardware_signature, addr);
+ ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr);
+ ACPI_READ_FIELD(facs_table->global_lock, addr);
+ ACPI_READ_FIELD(facs_table->flags, addr);
+ ACPI_READ_ARRAY(facs_table->resverved3, addr);
+
+ g_assert_cmphex(facs_table->signature, ==, ACPI_FACS_SIGNATURE);
+}
+
+static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr)
+{
+ uint8_t checksum;
+
+ ACPI_READ_TABLE_HEADER(&sdt_table->header, addr);
+
+ sdt_table->aml_len = sdt_table->header.length - sizeof(AcpiTableHeader);
+ sdt_table->aml = g_malloc0(sdt_table->aml_len);
+ ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr);
+
+ checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) +
+ acpi_checksum(sdt_table->aml, sdt_table->aml_len);
+ g_assert(!checksum);
+}
+
+static void test_acpi_dsdt_table(test_data *data)
+{
+ AcpiSdtTable *dsdt_table = &data->dsdt_table;
+ uint32_t addr = data->fadt_table.dsdt;
+
+ test_dst_table(dsdt_table, addr);
+ g_assert_cmphex(dsdt_table->header.signature, ==, ACPI_DSDT_SIGNATURE);
+}
+
+static void test_acpi_ssdt_tables(test_data *data)
+{
+ GArray *ssdt_tables;
+ int ssdt_tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
+ int i;
+
+ ssdt_tables = g_array_sized_new(false, true, sizeof(AcpiSdtTable),
+ ssdt_tables_nr);
+ for (i = 0; i < ssdt_tables_nr; i++) {
+ AcpiSdtTable ssdt_table;
+ uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */
+ test_dst_table(&ssdt_table, addr);
+ g_array_append_val(ssdt_tables, ssdt_table);
+ }
+ data->ssdt_tables = ssdt_tables;
+}
+
+static void test_acpi_one(const char *params, test_data *data)
+{
+ char *args;
+ uint8_t signature_low;
+ uint8_t signature_high;
+ uint16_t signature;
+ int i;
+
+ memset(data, 0, sizeof(*data));
+ args = g_strdup_printf("-net none -display none %s %s",
+ params ? params : "", disk);
+ qtest_start(args);
+
+ /* Wait at most 1 minute */
+#define TEST_DELAY (1 * G_USEC_PER_SEC / 10)
+#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1)
+
+ /* Poll until code has run and modified memory. Once it has we know BIOS
+ * initialization is done. TODO: check that IP reached the halt
+ * instruction.
+ */
+ for (i = 0; i < TEST_CYCLES; ++i) {
+ signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET);
+ signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1);
+ signature = (signature_high << 8) | signature_low;
+ if (signature == SIGNATURE) {
+ break;
+ }
+ g_usleep(TEST_DELAY);
+ }
+ g_assert_cmphex(signature, ==, SIGNATURE);
+
+ test_acpi_rsdp_address(data);
+ test_acpi_rsdp_table(data);
+ test_acpi_rsdt_table(data);
+ test_acpi_fadt_table(data);
+ test_acpi_facs_table(data);
+ test_acpi_dsdt_table(data);
+ test_acpi_ssdt_tables(data);
+
+ qtest_quit(global_qtest);
+ g_free(args);
+}
+
+static void test_acpi_tcg(void)
+{
+ test_data data;
+
+ /* Supplying -machine accel argument overrides the default (qtest).
+ * This is to make guest actually run.
+ */
+ test_acpi_one("-machine accel=tcg", &data);
+
+ free_test_data(&data);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *arch = qtest_get_arch();
+ FILE *f = fopen(disk, "w");
+ int ret;
+ fwrite(boot_sector, 1, sizeof boot_sector, f);
+ fclose(f);
+
+ g_test_init(&argc, &argv, NULL);
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qtest_add_func("acpi/tcg", test_acpi_tcg);
+ }
+ ret = g_test_run();
+ unlink(disk);
+ return ret;
+}
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index dc5f05a85f..7a7461b0b2 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -227,6 +227,160 @@ static void qdict_iterapi_test(void)
QDECREF(tests_dict);
}
+static void qdict_flatten_test(void)
+{
+ QList *list1 = qlist_new();
+ QList *list2 = qlist_new();
+ QDict *dict1 = qdict_new();
+ QDict *dict2 = qdict_new();
+ QDict *dict3 = qdict_new();
+
+ /*
+ * Test the flattening of
+ *
+ * {
+ * "e": [
+ * 42,
+ * [
+ * 23,
+ * 66,
+ * {
+ * "a": 0,
+ * "b": 1
+ * }
+ * ]
+ * ],
+ * "f": {
+ * "c": 2,
+ * "d": 3,
+ * },
+ * "g": 4
+ * }
+ *
+ * to
+ *
+ * {
+ * "e.0": 42,
+ * "e.1.0": 23,
+ * "e.1.1": 66,
+ * "e.1.2.a": 0,
+ * "e.1.2.b": 1,
+ * "f.c": 2,
+ * "f.d": 3,
+ * "g": 4
+ * }
+ */
+
+ qdict_put(dict1, "a", qint_from_int(0));
+ qdict_put(dict1, "b", qint_from_int(1));
+
+ qlist_append_obj(list1, QOBJECT(qint_from_int(23)));
+ qlist_append_obj(list1, QOBJECT(qint_from_int(66)));
+ qlist_append_obj(list1, QOBJECT(dict1));
+ qlist_append_obj(list2, QOBJECT(qint_from_int(42)));
+ qlist_append_obj(list2, QOBJECT(list1));
+
+ qdict_put(dict2, "c", qint_from_int(2));
+ qdict_put(dict2, "d", qint_from_int(3));
+ qdict_put_obj(dict3, "e", QOBJECT(list2));
+ qdict_put_obj(dict3, "f", QOBJECT(dict2));
+ qdict_put(dict3, "g", qint_from_int(4));
+
+ qdict_flatten(dict3);
+
+ g_assert(qdict_get_int(dict3, "e.0") == 42);
+ g_assert(qdict_get_int(dict3, "e.1.0") == 23);
+ g_assert(qdict_get_int(dict3, "e.1.1") == 66);
+ g_assert(qdict_get_int(dict3, "e.1.2.a") == 0);
+ g_assert(qdict_get_int(dict3, "e.1.2.b") == 1);
+ g_assert(qdict_get_int(dict3, "f.c") == 2);
+ g_assert(qdict_get_int(dict3, "f.d") == 3);
+ g_assert(qdict_get_int(dict3, "g") == 4);
+
+ g_assert(qdict_size(dict3) == 8);
+
+ QDECREF(dict3);
+}
+
+static void qdict_array_split_test(void)
+{
+ QDict *test_dict = qdict_new();
+ QDict *dict1, *dict2;
+ QList *test_list;
+
+ /*
+ * Test the split of
+ *
+ * {
+ * "1.x": 0,
+ * "3.y": 1,
+ * "0.a": 42,
+ * "o.o": 7,
+ * "0.b": 23
+ * }
+ *
+ * to
+ *
+ * [
+ * {
+ * "a": 42,
+ * "b": 23
+ * },
+ * {
+ * "x": 0
+ * }
+ * ]
+ *
+ * and
+ *
+ * {
+ * "3.y": 1,
+ * "o.o": 7
+ * }
+ *
+ * (remaining in the old QDict)
+ *
+ * This example is given in the comment of qdict_array_split().
+ */
+
+ qdict_put(test_dict, "1.x", qint_from_int(0));
+ qdict_put(test_dict, "3.y", qint_from_int(1));
+ qdict_put(test_dict, "0.a", qint_from_int(42));
+ qdict_put(test_dict, "o.o", qint_from_int(7));
+ qdict_put(test_dict, "0.b", qint_from_int(23));
+
+ qdict_array_split(test_dict, &test_list);
+
+ dict1 = qobject_to_qdict(qlist_pop(test_list));
+ dict2 = qobject_to_qdict(qlist_pop(test_list));
+
+ g_assert(dict1);
+ g_assert(dict2);
+ g_assert(qlist_empty(test_list));
+
+ QDECREF(test_list);
+
+ g_assert(qdict_get_int(dict1, "a") == 42);
+ g_assert(qdict_get_int(dict1, "b") == 23);
+
+ g_assert(qdict_size(dict1) == 2);
+
+ QDECREF(dict1);
+
+ g_assert(qdict_get_int(dict2, "x") == 0);
+
+ g_assert(qdict_size(dict2) == 1);
+
+ QDECREF(dict2);
+
+ g_assert(qdict_get_int(test_dict, "3.y") == 1);
+ g_assert(qdict_get_int(test_dict, "o.o") == 7);
+
+ g_assert(qdict_size(test_dict) == 2);
+
+ QDECREF(test_dict);
+}
+
/*
* Errors test-cases
*/
@@ -365,6 +519,8 @@ int main(int argc, char **argv)
g_test_add_func("/public/del", qdict_del_test);
g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
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("/errors/put_exists", qdict_put_exists_test);
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
diff --git a/tests/check-qom-interface.c b/tests/check-qom-interface.c
new file mode 100644
index 0000000000..f06380ef14
--- /dev/null
+++ b/tests/check-qom-interface.c
@@ -0,0 +1,105 @@
+/*
+ * QOM interface test.
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ * Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <glib.h>
+
+#include "qom/object.h"
+#include "qemu/module.h"
+
+
+#define TYPE_TEST_IF "test-interface"
+#define TEST_IF_CLASS(klass) \
+ OBJECT_CLASS_CHECK(TestIfClass, (klass), TYPE_TEST_IF)
+#define TEST_IF_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(TestIfClass, (obj), TYPE_TEST_IF)
+#define TEST_IF(obj) \
+ INTERFACE_CHECK(TestIf, (obj), TYPE_TEST_IF)
+
+typedef struct TestIf {
+ Object parent_obj;
+} TestIf;
+
+typedef struct TestIfClass {
+ InterfaceClass parent_class;
+
+ uint32_t test;
+} TestIfClass;
+
+static const TypeInfo test_if_info = {
+ .name = TYPE_TEST_IF,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(TestIfClass),
+};
+
+#define PATTERN 0xFAFBFCFD
+
+static void test_class_init(ObjectClass *oc, void *data)
+{
+ TestIfClass *tc = TEST_IF_CLASS(oc);
+
+ g_assert(tc);
+ tc->test = PATTERN;
+}
+
+#define TYPE_DIRECT_IMPL "direct-impl"
+
+static const TypeInfo direct_impl_info = {
+ .name = TYPE_DIRECT_IMPL,
+ .parent = TYPE_OBJECT,
+ .class_init = test_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_TEST_IF },
+ { }
+ }
+};
+
+#define TYPE_INTERMEDIATE_IMPL "intermediate-impl"
+
+static const TypeInfo intermediate_impl_info = {
+ .name = TYPE_INTERMEDIATE_IMPL,
+ .parent = TYPE_DIRECT_IMPL,
+};
+
+static void test_interface_impl(const char *type)
+{
+ Object *obj = object_new(type);
+ TestIf *iobj = TEST_IF(obj);
+ TestIfClass *ioc = TEST_IF_GET_CLASS(iobj);
+
+ g_assert(iobj);
+ g_assert(ioc->test == PATTERN);
+}
+
+static void interface_direct_test(void)
+{
+ test_interface_impl(TYPE_DIRECT_IMPL);
+}
+
+static void interface_intermediate_test(void)
+{
+ test_interface_impl(TYPE_INTERMEDIATE_IMPL);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ module_call_init(MODULE_INIT_QOM);
+ type_register_static(&test_if_info);
+ type_register_static(&direct_impl_info);
+ type_register_static(&intermediate_impl_info);
+
+ g_test_add_func("/qom/interface/direct_impl", interface_direct_test);
+ g_test_add_func("/qom/interface/intermediate_impl",
+ interface_intermediate_test);
+
+ return g_test_run();
+}
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 38b5b178d0..37096dcc13 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -518,7 +518,6 @@ static void fuzz_registers(void)
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
- char *cmdline;
int fd;
int ret;
@@ -538,9 +537,7 @@ int main(int argc, char **argv)
/* Run the tests */
g_test_init(&argc, &argv, NULL);
- cmdline = g_strdup_printf("-vnc none ");
-
- qtest_start(cmdline);
+ qtest_start(NULL);
qtest_irq_intercept_in(global_qtest, "ioapic");
qtest_add_func("/fdc/cmos", test_cmos);
qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 65c786ca1e..fa3e3d6b87 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -2,9 +2,11 @@
* qtest I440FX test case
*
* Copyright IBM, Corp. 2012-2013
+ * Copyright Red Hat, Inc. 2013
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
+ * Laszlo Ersek <lersek@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.
@@ -18,6 +20,11 @@
#include <glib.h>
#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdlib.h>
#define BROKEN 1
@@ -26,16 +33,32 @@
typedef struct TestData
{
int num_cpus;
- QPCIBus *bus;
} TestData;
+typedef struct FirmwareTestFixture {
+ /* decides whether we're testing -bios or -pflash */
+ bool is_bios;
+} FirmwareTestFixture;
+
+static QPCIBus *test_start_get_bus(const TestData *s)
+{
+ char *cmdline;
+
+ cmdline = g_strdup_printf("-smp %d", s->num_cpus);
+ qtest_start(cmdline);
+ g_free(cmdline);
+ return qpci_init_pc();
+}
+
static void test_i440fx_defaults(gconstpointer opaque)
{
const TestData *s = opaque;
+ QPCIBus *bus;
QPCIDevice *dev;
uint32_t value;
- dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+ bus = test_start_get_bus(s);
+ dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
g_assert(dev != NULL);
/* 3.2.2 */
@@ -119,6 +142,8 @@ static void test_i440fx_defaults(gconstpointer opaque)
g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */
/* 3.2.26 */
g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */
+
+ qtest_end();
}
#define PAM_RE 1
@@ -177,6 +202,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
static void test_i440fx_pam(gconstpointer opaque)
{
const TestData *s = opaque;
+ QPCIBus *bus;
QPCIDevice *dev;
int i;
static struct {
@@ -199,7 +225,8 @@ static void test_i440fx_pam(gconstpointer opaque)
{ 0xEC000, 0xEFFFF }, /* BIOS Extension */
};
- dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+ bus = test_start_get_bus(s);
+ dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
g_assert(dev != NULL);
for (i = 0; i < ARRAY_SIZE(pam_area); i++) {
@@ -252,34 +279,140 @@ static void test_i440fx_pam(gconstpointer opaque)
/* Verify the area is not our new mask */
g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82));
}
+ qtest_end();
+}
+
+#define BLOB_SIZE ((size_t)65536)
+#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024))
+
+/* Create a blob file, and return its absolute pathname as a dynamically
+ * allocated string.
+ * The file is closed before the function returns.
+ * In case of error, NULL is returned. The function prints the error message.
+ */
+static char *create_blob_file(void)
+{
+ int ret, fd;
+ char *pathname;
+ GError *error = NULL;
+
+ ret = -1;
+ fd = g_file_open_tmp("blob_XXXXXX", &pathname, &error);
+ if (fd == -1) {
+ fprintf(stderr, "unable to create blob file: %s\n", error->message);
+ g_error_free(error);
+ } else {
+ if (ftruncate(fd, BLOB_SIZE) == -1) {
+ fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname,
+ BLOB_SIZE, strerror(errno));
+ } else {
+ void *buf;
+
+ buf = mmap(NULL, BLOB_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buf == MAP_FAILED) {
+ fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, BLOB_SIZE,
+ strerror(errno));
+ } else {
+ size_t i;
+
+ for (i = 0; i < BLOB_SIZE; ++i) {
+ ((uint8_t *)buf)[i] = i;
+ }
+ munmap(buf, BLOB_SIZE);
+ ret = 0;
+ }
+ }
+ close(fd);
+ if (ret == -1) {
+ unlink(pathname);
+ g_free(pathname);
+ }
+ }
+
+ return ret == -1 ? NULL : pathname;
+}
+
+static void test_i440fx_firmware(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
+{
+ char *fw_pathname, *cmdline;
+ uint8_t *buf;
+ size_t i, isa_bios_size;
+
+ fw_pathname = create_blob_file();
+ g_assert(fw_pathname != NULL);
+
+ /* Better hope the user didn't put metacharacters in TMPDIR and co. */
+ cmdline = g_strdup_printf("-S %s %s",
+ fixture->is_bios ? "-bios" : "-pflash",
+ fw_pathname);
+ g_test_message("qemu cmdline: %s", cmdline);
+ qtest_start(cmdline);
+ g_free(cmdline);
+
+ /* Qemu has loaded the firmware (because qtest_start() only returns after
+ * the QMP handshake completes). We must unlink the firmware blob right
+ * here, because any assertion firing below would leak it in the
+ * filesystem. This is also the reason why we recreate the blob every time
+ * this function is invoked.
+ */
+ unlink(fw_pathname);
+ g_free(fw_pathname);
+
+ /* check below 4G */
+ buf = g_malloc0(BLOB_SIZE);
+ memread(0x100000000ULL - BLOB_SIZE, buf, BLOB_SIZE);
+ for (i = 0; i < BLOB_SIZE; ++i) {
+ g_assert_cmphex(buf[i], ==, (uint8_t)i);
+ }
+
+ /* check in ISA space too */
+ memset(buf, 0, BLOB_SIZE);
+ isa_bios_size = ISA_BIOS_MAXSZ < BLOB_SIZE ? ISA_BIOS_MAXSZ : BLOB_SIZE;
+ memread(0x100000 - isa_bios_size, buf, isa_bios_size);
+ for (i = 0; i < isa_bios_size; ++i) {
+ g_assert_cmphex(buf[i], ==,
+ (uint8_t)((BLOB_SIZE - isa_bios_size) + i));
+ }
+
+ g_free(buf);
+ qtest_end();
+}
+
+static void add_firmware_test(const char *testpath,
+ void (*setup_fixture)(FirmwareTestFixture *f,
+ gconstpointer test_data))
+{
+ g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
+ test_i440fx_firmware, NULL);
+}
+
+static void request_bios(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
+{
+ fixture->is_bios = true;
+}
+
+static void request_pflash(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
+{
+ fixture->is_bios = false;
}
int main(int argc, char **argv)
{
- QTestState *s;
TestData data;
- char *cmdline;
int ret;
g_test_init(&argc, &argv, NULL);
data.num_cpus = 1;
- cmdline = g_strdup_printf("-smp %d", data.num_cpus);
- s = qtest_start(cmdline);
- g_free(cmdline);
-
- data.bus = qpci_init_pc();
-
g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
-
+ add_firmware_test("/i440fx/firmware/bios", request_bios);
+ add_firmware_test("/i440fx/firmware/pflash", request_pflash);
ret = g_test_run();
-
- if (s) {
- qtest_quit(s);
- }
-
return ret;
}
diff --git a/tests/ide-test.c b/tests/ide-test.c
index d5cec5a1fc..4a0d97f197 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -380,7 +380,6 @@ static void test_bmdma_no_busmaster(void)
static void test_bmdma_setup(void)
{
ide_test_start(
- "-vnc none "
"-drive file=%s,if=ide,serial=%s,cache=writeback "
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
@@ -410,7 +409,6 @@ static void test_identify(void)
int ret;
ide_test_start(
- "-vnc none "
"-drive file=%s,if=ide,serial=%s,cache=writeback "
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
@@ -455,7 +453,6 @@ static void test_flush(void)
uint8_t data;
ide_test_start(
- "-vnc none "
"-drive file=blkdebug::%s,if=ide,cache=writeback",
tmp_path);
diff --git a/tests/qdev-monitor-test.c b/tests/qdev-monitor-test.c
index 33a8ea4b9c..ba7f9cc238 100644
--- a/tests/qdev-monitor-test.c
+++ b/tests/qdev-monitor-test.c
@@ -32,10 +32,8 @@ static void test_device_add(void)
"}}");
g_assert(response);
error = qdict_get_qdict(response, "error");
- g_assert(!strcmp(qdict_get_try_str(error, "class") ?: "",
- "GenericError"));
g_assert(!strcmp(qdict_get_try_str(error, "desc") ?: "",
- "Device initialization failed."));
+ "Device needs media, but drive is empty"));
QDECREF(response);
/* Delete the drive */
diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017
index aba3faf712..3af3cdfbc3 100755
--- a/tests/qemu-iotests/017
+++ b/tests/qemu-iotests/017
@@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
TEST_OFFSETS="0 4294967296"
diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018
index 15fcfe5670..6f7f0545d0 100755
--- a/tests/qemu-iotests/018
+++ b/tests/qemu-iotests/018
@@ -43,6 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
TEST_OFFSETS="0 4294967296"
diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019
index 5bb18d0c0a..b43e70f3cb 100755
--- a/tests/qemu-iotests/019
+++ b/tests/qemu-iotests/019
@@ -47,6 +47,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
TEST_OFFSETS="0 4294967296"
CLUSTER_SIZE=65536
diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020
index b3c86d844e..73a0429481 100755
--- a/tests/qemu-iotests/020
+++ b/tests/qemu-iotests/020
@@ -45,6 +45,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
TEST_OFFSETS="0 4294967296"
diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034
index 67f1959690..7349789583 100755
--- a/tests/qemu-iotests/034
+++ b/tests/qemu-iotests/034
@@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
CLUSTER_SIZE=4k
size=128M
diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037
index 743bae33d3..e444349e6d 100755
--- a/tests/qemu-iotests/037
+++ b/tests/qemu-iotests/037
@@ -41,6 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
CLUSTER_SIZE=4k
size=128M
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 18dcd61ef2..72eaad5b08 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -39,6 +39,29 @@ class ImageCommitTestCase(iotests.QMPTestCase):
result = self.vm.qmp('query-block-jobs')
self.assert_qmp(result, 'return', [])
+ def run_commit_test(self, top, base):
+ self.assert_no_active_commit()
+ result = self.vm.qmp('block-commit', device='drive0', top=top, base=base)
+ self.assert_qmp(result, 'return', {})
+
+ completed = False
+ while not completed:
+ for event in self.vm.get_qmp_events(wait=True):
+ if event['event'] == 'BLOCK_JOB_COMPLETED':
+ self.assert_qmp(event, 'data/type', 'commit')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/offset', self.image_len)
+ self.assert_qmp(event, 'data/len', self.image_len)
+ completed = True
+ elif event['event'] == 'BLOCK_JOB_READY':
+ self.assert_qmp(event, 'data/type', 'commit')
+ self.assert_qmp(event, 'data/device', 'drive0')
+ self.assert_qmp(event, 'data/len', self.image_len)
+ self.vm.qmp('block-job-complete', device='drive0')
+
+ self.assert_no_active_commit()
+ self.vm.shutdown()
+
class TestSingleDrive(ImageCommitTestCase):
image_len = 1 * 1024 * 1024
test_len = 1 * 1024 * 256
@@ -59,23 +82,7 @@ class TestSingleDrive(ImageCommitTestCase):
os.remove(backing_img)
def test_commit(self):
- self.assert_no_active_commit()
- result = self.vm.qmp('block-commit', device='drive0', top='%s' % mid_img)
- self.assert_qmp(result, 'return', {})
-
- completed = False
- while not completed:
- for event in self.vm.get_qmp_events(wait=True):
- if event['event'] == 'BLOCK_JOB_COMPLETED':
- self.assert_qmp(event, 'data/type', 'commit')
- self.assert_qmp(event, 'data/device', 'drive0')
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
- completed = True
-
- self.assert_no_active_commit()
- self.vm.shutdown()
-
+ self.run_commit_test(mid_img, backing_img)
self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
@@ -102,10 +109,9 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
def test_top_is_active(self):
- self.assert_no_active_commit()
- result = self.vm.qmp('block-commit', device='drive0', top='%s' % test_img, base='%s' % backing_img)
- self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported')
+ self.run_commit_test(test_img, backing_img)
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
def test_top_and_base_reversed(self):
self.assert_no_active_commit()
@@ -166,23 +172,7 @@ class TestRelativePaths(ImageCommitTestCase):
raise
def test_commit(self):
- self.assert_no_active_commit()
- result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.mid_img)
- self.assert_qmp(result, 'return', {})
-
- completed = False
- while not completed:
- for event in self.vm.get_qmp_events(wait=True):
- if event['event'] == 'BLOCK_JOB_COMPLETED':
- self.assert_qmp(event, 'data/type', 'commit')
- self.assert_qmp(event, 'data/device', 'drive0')
- self.assert_qmp(event, 'data/offset', self.image_len)
- self.assert_qmp(event, 'data/len', self.image_len)
- completed = True
-
- self.assert_no_active_commit()
- self.vm.shutdown()
-
+ self.run_commit_test(self.mid_img, self.backing_img)
self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
@@ -209,10 +199,9 @@ class TestRelativePaths(ImageCommitTestCase):
self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found')
def test_top_is_active(self):
- self.assert_no_active_commit()
- result = self.vm.qmp('block-commit', device='drive0', top='%s' % self.test_img, base='%s' % self.backing_img)
- self.assert_qmp(result, 'error/class', 'GenericError')
- self.assert_qmp(result, 'error/desc', 'Top image as the active layer is currently unsupported')
+ self.run_commit_test(self.test_img, self.backing_img)
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
+ self.assertEqual(-1, qemu_io('-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
def test_top_and_base_reversed(self):
self.assert_no_active_commit()
@@ -229,6 +218,7 @@ class TestSetSpeed(ImageCommitTestCase):
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
qemu_io('-c', 'write -P 0x1 0 512', test_img)
+ qemu_io('-c', 'write -P 0xef 524288 524288', mid_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 49e95a20cf..d0c5173626 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -91,7 +91,6 @@ Testing: -drive if=virtio
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) QEMU_PROG: -drive if=virtio: Device needs media, but drive is empty
QEMU_PROG: -drive if=virtio: Device initialization failed.
-QEMU_PROG: -drive if=virtio: Device initialization failed.
QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
Testing: -drive if=scsi
@@ -223,7 +222,7 @@ 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: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
=== Parsing protocol from file name ===
diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059
index 73941c3e61..2d604d3a91 100755
--- a/tests/qemu-iotests/059
+++ b/tests/qemu-iotests/059
@@ -42,6 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt vmdk
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
capacity_offset=16
granularity_offset=20
@@ -81,10 +84,37 @@ IMGOPTS="subformat=twoGbMaxExtentFlat" _make_test_img 1000G
$QEMU_IMG info $TEST_IMG | _filter_testdir | sed -e 's/cid: [0-9]*/cid: XXXXXXXX/'
echo
+echo "=== Testing malformed VMFS extent description line ==="
+cat >"$TEST_IMG" <<EOF
+# Disk DescriptorFile
+version=1
+CID=58ab4847
+parentCID=ffffffff
+createType="vmfs"
+
+# Extent description
+RW 12582912 VMFS "dummy.vmdk" 1
+EOF
+_img_info
+
+echo
+echo "=== Testing truncated sparse ==="
+IMGOPTS="subformat=monolithicSparse" _make_test_img 100G
+truncate -s 10M $TEST_IMG
+_img_info
+
+echo
echo "=== Testing version 3 ==="
_use_sample_img iotest-version3.vmdk.bz2
_img_info
+echo
+echo "=== Testing 4TB monolithicFlat creation and IO ==="
+IMGOPTS="subformat=monolithicFlat" _make_test_img 4T
+_img_info
+$QEMU_IO -c "write -P 0xa 900G 512" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "read -v 900G 1024" "$TEST_IMG" | _filter_qemu_io
+
# success, all done
echo "*** done"
rm -f $seq.full
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
index 4ff935c6f4..4ffeb54710 100644
--- a/tests/qemu-iotests/059.out
+++ b/tests/qemu-iotests/059.out
@@ -2038,8 +2038,92 @@ Format specific information:
filename: TEST_DIR/t-f500.vmdk
format: FLAT
+=== Testing malformed VMFS extent description line ===
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid extent lines:
+RW 12582912 VMFS "dummy.IMGFMT" 1
+
+
+=== Testing truncated sparse ===
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=107374182400
+qemu-img: File truncated, expecting at least 13172736 bytes
+qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'TEST_DIR/t.IMGFMT': Wrong medium type
+
=== Testing version 3 ===
image: TEST_DIR/iotest-version3.IMGFMT
file format: IMGFMT
virtual size: 1.0G (1073741824 bytes)
+
+=== Testing 4TB monolithicFlat creation and IO ===
+Formatting 'TEST_DIR/iotest-version3.IMGFMT', fmt=IMGFMT size=4398046511104
+image: TEST_DIR/iotest-version3.IMGFMT
+file format: IMGFMT
+virtual size: 4.0T (4398046511104 bytes)
+wrote 512/512 bytes at offset 966367641600
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+e100000000: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000010: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000020: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000030: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000040: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000050: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000060: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000070: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000080: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000090: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000a0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000b0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000c0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000d0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000e0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000000f0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000100: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000110: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000120: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000130: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000140: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000150: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000160: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000170: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000180: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000190: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001a0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001b0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001c0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001d0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001e0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e1000001f0: 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a ................
+e100000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000220: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000002f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000350: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000360: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e100000390: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+e1000003f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+read 1024/1024 bytes at offset 966367641600
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063
index 2ab8f20e02..77503a2984 100755
--- a/tests/qemu-iotests/063
+++ b/tests/qemu-iotests/063
@@ -44,6 +44,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt qcow qcow2 vmdk qed raw
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" \
+ "subformat=twoGbMaxExtentFlat" \
+ "subformat=twoGbMaxExtentSparse"
_make_test_img 4M
diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069
index 3042803a81..50347d91d2 100755
--- a/tests/qemu-iotests/069
+++ b/tests/qemu-iotests/069
@@ -41,6 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt cow qed qcow qcow2 vmdk
_supported_proto generic
_supported_os Linux
+_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat"
IMG_SIZE=128K
diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071
new file mode 100755
index 0000000000..2a22546e1a
--- /dev/null
+++ b/tests/qemu-iotests/071
@@ -0,0 +1,239 @@
+#!/bin/bash
+#
+# Test case for the QMP blkdebug and blkverify interfaces
+#
+# Copyright (C) 2013 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 generic
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@" | _filter_imgfmt
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu_io
+}
+
+IMG_SIZE=64M
+
+echo
+echo "=== Testing blkverify through filename ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
+ _filter_imgfmt
+_make_test_img $IMG_SIZE
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+ -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
+
+$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+ -c 'read -P 42 0 512' | _filter_qemu_io
+
+echo
+echo "=== Testing blkverify through file blockref ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" IMGOPTS="" IMGFMT="raw" _make_test_img $IMG_SIZE |\
+ _filter_imgfmt
+_make_test_img $IMG_SIZE
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base,file.test.driver=$IMGFMT,file.test.file.filename=$TEST_IMG" \
+ -c 'read 0 512' -c 'write -P 42 0x38000 512' -c 'read -P 42 0x38000 512' | _filter_qemu_io
+
+$QEMU_IO -c 'write -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO -c "open -o file.driver=blkverify,file.raw.filename=$TEST_IMG.base $TEST_IMG" \
+ -c 'read -P 42 0 512' | _filter_qemu_io
+
+echo
+echo "=== Testing blkdebug through filename ==="
+echo
+
+$QEMU_IO -c "open -o file.driver=blkdebug,file.inject-error.event=l2_load $TEST_IMG" \
+ -c 'read -P 42 0x38000 512'
+
+echo
+echo "=== Testing blkdebug through file blockref ==="
+echo
+
+$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=blkdebug,file.inject-error.event=l2_load,file.image.filename=$TEST_IMG" \
+ -c 'read -P 42 0x38000 512'
+
+echo
+echo "=== Testing blkdebug on existing block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "$IMGFMT",
+ "id": "drive0-debug",
+ "file": {
+ "driver": "blkdebug",
+ "image": "drive0",
+ "inject-error": [{
+ "event": "l2_load"
+ }]
+ }
+ }
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-debug "read 0 512"'
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkverify on existing block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=$IMGFMT,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "blkverify",
+ "id": "drive0-verify",
+ "test": "drive0",
+ "raw": {
+ "driver": "raw",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG.base"
+ }
+ }
+ }
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-verify "read 0 512"'
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkverify on existing raw block device ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG.base,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "blkverify",
+ "id": "drive0-verify",
+ "test": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG"
+ }
+ },
+ "raw": "drive0"
+ }
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-verify "read 0 512"'
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Testing blkdebug's set-state through QMP ==="
+echo
+
+run_qemu -drive "file=$TEST_IMG,format=raw,if=none,id=drive0" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "blockdev-add",
+ "arguments": {
+ "options": {
+ "driver": "$IMGFMT",
+ "id": "drive0-debug",
+ "file": {
+ "driver": "blkdebug",
+ "image": "drive0",
+ "inject-error": [{
+ "event": "read_aio",
+ "state": 42
+ }],
+ "set-state": [{
+ "event": "write_aio",
+ "new_state": 42
+ }]
+ }
+ }
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-debug "read 0 512"'
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-debug "write 0 512"'
+ }
+}
+{ "execute": "human-monitor-command",
+ "arguments": {
+ "command-line": 'qemu-io drive0-debug "read 0 512"'
+ }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out
new file mode 100644
index 0000000000..5f840a9980
--- /dev/null
+++ b/tests/qemu-iotests/071.out
@@ -0,0 +1,90 @@
+QA output created by 071
+
+=== Testing blkverify through filename ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+=== Testing blkverify through file blockref ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 229376
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+=== Testing blkdebug through filename ===
+
+read failed: Input/output error
+
+=== Testing blkdebug through file blockref ===
+
+read failed: Input/output error
+
+=== Testing blkdebug on existing block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+read failed: Input/output error
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+
+=== Testing blkverify on existing block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=IMGFMT,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
+
+
+=== Testing blkverify on existing raw block device ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT.base,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+blkverify: read sector_num=0 nb_sectors=1 contents mismatch in sector 0
+
+
+=== Testing blkdebug's set-state through QMP ===
+
+Testing: -drive file=TEST_DIR/t.IMGFMT,format=raw,if=none,id=drive0
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+read failed: Input/output error
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "floppy0", "tray-open": true}}
+
+*** done
diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072
new file mode 100755
index 0000000000..a3876c2161
--- /dev/null
+++ b/tests/qemu-iotests/072
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Test case for nested image formats
+#
+# Copyright (C) 2013 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 vpc vmdk vhdx vdi qed qcow2 qcow cow
+_supported_proto generic
+_supported_os Linux
+
+IMG_SIZE=64M
+
+echo
+echo "=== Testing nested image formats ==="
+echo
+
+TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
+
+$QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \
+ -c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io
+
+$QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG"
+
+$QEMU_IO -c "open -o driver=$IMGFMT,file.driver=$IMGFMT,file.file.filename=$TEST_IMG" \
+ -c 'read -P 42 0 512' -c 'read -P 23 512 512' \
+ -c 'read -P 66 1024 512' | _filter_qemu_io
+
+# When not giving any format, qemu should open only one "layer". Therefore, this
+# should not work for any image formats with a header.
+$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/072.out b/tests/qemu-iotests/072.out
new file mode 100644
index 0000000000..efe577c1c0
--- /dev/null
+++ b/tests/qemu-iotests/072.out
@@ -0,0 +1,21 @@
+QA output created by 072
+
+=== Testing nested image formats ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 1024
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 1024
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Pattern verification failed at offset 0, 512 bytes
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077
new file mode 100755
index 0000000000..bbf7b5145a
--- /dev/null
+++ b/tests/qemu-iotests/077
@@ -0,0 +1,278 @@
+#!/bin/bash
+#
+# Test concurrent pread/pwrite
+#
+# Copyright (C) 2014 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=kwolf@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 generic
+_supported_os Linux
+
+CLUSTER_SIZE=4k
+size=128M
+
+_make_test_img $size
+
+echo
+echo "== Some concurrent requests involving RMW =="
+
+function test_io()
+{
+echo "open -o file.align=4k blkdebug::$TEST_IMG"
+# A simple RMW request
+cat <<EOF
+aio_write -P 10 0x200 0x200
+aio_flush
+EOF
+
+# Sequential RMW requests on the same physical sector
+off=0x1000
+for ev in "head" "after_head" "tail" "after_tail"; do
+cat <<EOF
+break pwritev_rmw.$ev A
+aio_write -P 10 $((off + 0x200)) 0x200
+wait_break A
+aio_write -P 11 $((off + 0x400)) 0x200
+sleep 100
+resume A
+aio_flush
+EOF
+off=$((off + 0x1000))
+done
+
+# Chained dependencies
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x5000 0x200
+wait_break A
+aio_write -P 11 0x5200 0x200
+aio_write -P 12 0x5400 0x200
+aio_write -P 13 0x5600 0x200
+aio_write -P 14 0x5800 0x200
+aio_write -P 15 0x5a00 0x200
+aio_write -P 16 0x5c00 0x200
+aio_write -P 17 0x5e00 0x200
+sleep 100
+resume A
+aio_flush
+EOF
+
+# Overlapping multiple requests
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x6000 0x200
+wait_break A
+break pwritev_rmw.after_head B
+aio_write -P 10 0x7e00 0x200
+wait_break B
+aio_write -P 11 0x6800 0x1000
+resume A
+sleep 100
+resume B
+aio_flush
+EOF
+
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0x8000 0x200
+wait_break A
+break pwritev_rmw.after_head B
+aio_write -P 10 0x9e00 0x200
+wait_break B
+aio_write -P 11 0x8800 0x1000
+resume B
+sleep 100
+resume A
+aio_flush
+EOF
+
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xa000 0x200
+wait_break A
+aio_write -P 11 0xa800 0x1000
+break pwritev_rmw.after_head B
+aio_write -P 10 0xbe00 0x200
+wait_break B
+resume A
+sleep 100
+resume B
+aio_flush
+EOF
+
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xc000 0x200
+wait_break A
+aio_write -P 11 0xc800 0x1000
+break pwritev_rmw.after_head B
+aio_write -P 10 0xde00 0x200
+wait_break B
+resume B
+sleep 100
+resume A
+aio_flush
+EOF
+
+# Only RMW for the tail part
+cat <<EOF
+break pwritev_rmw.after_tail A
+aio_write -P 10 0xe000 0x1800
+wait_break A
+aio_write -P 11 0xf000 0xc00
+sleep 100
+resume A
+aio_flush
+EOF
+
+cat <<EOF
+break pwritev A
+aio_write -P 10 0x10000 0x800
+wait_break A
+break pwritev_rmw.after_tail B
+aio_write -P 11 0x10000 0x400
+break pwritev_done C
+resume A
+wait_break C
+resume C
+sleep 100
+wait_break B
+resume B
+aio_flush
+EOF
+
+cat <<EOF
+break pwritev A
+aio_write -P 10 0x11000 0x800
+wait_break A
+aio_write -P 11 0x11000 0x1000
+sleep 100
+resume A
+aio_flush
+EOF
+}
+
+test_io | $QEMU_IO | _filter_qemu_io | \
+ sed -e 's,[0-9/]* bytes at offset [0-9]*,XXX/XXX bytes at offset XXX,g' \
+ -e 's/^[0-9]* \(bytes\|KiB\)/XXX bytes/' \
+ -e '/Suspended/d'
+
+echo
+echo "== Verify image content =="
+
+function verify_io()
+{
+ # A simple RMW request
+ echo read -P 0 0 0x200
+ echo read -P 10 0x200 0x200
+ echo read -P 0 0x400 0xc00
+
+ # Sequential RMW requests on the same physical sector
+ echo read -P 0 0x1000 0x200
+ echo read -P 10 0x1200 0x200
+ echo read -P 11 0x1400 0x200
+ echo read -P 0 0x1600 0xa00
+
+ echo read -P 0 0x2000 0x200
+ echo read -P 10 0x2200 0x200
+ echo read -P 11 0x2400 0x200
+ echo read -P 0 0x2600 0xa00
+
+ echo read -P 0 0x3000 0x200
+ echo read -P 10 0x3200 0x200
+ echo read -P 11 0x3400 0x200
+ echo read -P 0 0x3600 0xa00
+
+ echo read -P 0 0x4000 0x200
+ echo read -P 10 0x4200 0x200
+ echo read -P 11 0x4400 0x200
+ echo read -P 0 0x4600 0xa00
+
+ # Chained dependencies
+ echo read -P 10 0x5000 0x200
+ echo read -P 11 0x5200 0x200
+ echo read -P 12 0x5400 0x200
+ echo read -P 13 0x5600 0x200
+ echo read -P 14 0x5800 0x200
+ echo read -P 15 0x5a00 0x200
+ echo read -P 16 0x5c00 0x200
+ echo read -P 17 0x5e00 0x200
+
+ # Overlapping multiple requests
+ echo read -P 10 0x6000 0x200
+ echo read -P 0 0x6200 0x600
+ echo read -P 11 0x6800 0x1000
+ echo read -P 0 0x7800 0x600
+ echo read -P 10 0x7e00 0x200
+
+ echo read -P 10 0x8000 0x200
+ echo read -P 0 0x8200 0x600
+ echo read -P 11 0x8800 0x1000
+ echo read -P 0 0x9800 0x600
+ echo read -P 10 0x9e00 0x200
+
+ echo read -P 10 0xa000 0x200
+ echo read -P 0 0xa200 0x600
+ echo read -P 11 0xa800 0x1000
+ echo read -P 0 0xb800 0x600
+ echo read -P 10 0xbe00 0x200
+
+ echo read -P 10 0xc000 0x200
+ echo read -P 0 0xc200 0x600
+ echo read -P 11 0xc800 0x1000
+ echo read -P 0 0xd800 0x600
+ echo read -P 10 0xde00 0x200
+
+ # Only RMW for the tail part
+ echo read -P 10 0xe000 0x1000
+ echo read -P 11 0xf800 0x400
+ echo read -P 0 0xfc00 0x400
+
+ echo read -P 11 0x10000 0x400
+ echo read -P 10 0x10400 0x400
+
+ echo read -P 11 0x11800 0x800
+}
+
+verify_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io
+
+_check_test_img
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/077.out b/tests/qemu-iotests/077.out
new file mode 100644
index 0000000000..ab612344d6
--- /dev/null
+++ b/tests/qemu-iotests/077.out
@@ -0,0 +1,202 @@
+QA output created by 077
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
+
+== Some concurrent requests involving RMW ==
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+blkdebug: Resuming request 'C'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'B'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+blkdebug: Resuming request 'A'
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote XXX/XXX bytes at offset XXX
+XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+== Verify image content ==
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 512
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 3072/3072 bytes at offset 1024
+3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 4096
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 4608
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 5120
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 5632
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 8192
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 8704
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 9216
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 9728
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 12288
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 12800
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 13312
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 13824
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 16384
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 16896
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 17408
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2560/2560 bytes at offset 17920
+2.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20480
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 20992
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 21504
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22016
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 22528
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 23040
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 23552
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24064
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 24576
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 25088
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 26624
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 30720
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32256
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 32768
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 33280
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 34816
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 38912
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40448
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 40960
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 41472
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 43008
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 47104
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 48640
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 49152
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 49664
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 51200
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1536/1536 bytes at offset 55296
+1.500 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 56832
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 4096/4096 bytes at offset 57344
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 63488
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 64512
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 65536
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 1024/1024 bytes at offset 66560
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 2048/2048 bytes at offset 71680
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+*** done
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 28ba0d9ad5..0f68156400 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -170,6 +170,17 @@ _make_test_img()
fi
}
+_rm_test_img()
+{
+ local img=$1
+ if [ "$IMGFMT" = "vmdk" ]; then
+ # Remove all the extents for vmdk
+ $QEMU_IMG info $img 2>/dev/null | grep 'filename:' | cut -f 2 -d: \
+ | xargs -I {} rm -f "{}"
+ fi
+ rm -f $img
+}
+
_cleanup_test_img()
{
case "$IMGPROTO" in
@@ -179,9 +190,9 @@ _cleanup_test_img()
rm -f "$TEST_IMG_FILE"
;;
file)
- rm -f "$TEST_DIR/t.$IMGFMT"
- rm -f "$TEST_DIR/t.$IMGFMT.orig"
- rm -f "$TEST_DIR/t.$IMGFMT.base"
+ _rm_test_img "$TEST_DIR/t.$IMGFMT"
+ _rm_test_img "$TEST_DIR/t.$IMGFMT.orig"
+ _rm_test_img "$TEST_DIR/t.$IMGFMT.base"
if [ -n "$SAMPLE_IMG_FILE" ]
then
rm -f "$TEST_DIR/$SAMPLE_IMG_FILE"
@@ -406,6 +417,17 @@ _default_cache_mode()
fi
}
+_unsupported_imgopts()
+{
+ for bad_opt
+ do
+ if echo "$IMGOPTS" | grep -q 2>/dev/null "$bad_opt"
+ then
+ _notrun "not suitable for image option: $bad_opt"
+ fi
+ done
+}
+
# this test requires that a specified command (executable) exists
#
_require_command()
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index cc750c986e..03c762fb4f 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -77,5 +77,8 @@
068 rw auto
069 rw auto
070 rw auto
+071 rw auto
+072 rw auto
073 rw auto
074 rw auto
+077 rw auto
diff --git a/tests/qom-test.c b/tests/qom-test.c
index 499be40261..5e5af7a50f 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -70,6 +70,8 @@ static const char *arm_machines[] = {
"xilinx-zynq-a9",
"highbank",
"midway",
+ "canon-a1100",
+ "cubieboard",
};
static const char *cris_machines[] = {
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
new file mode 100644
index 0000000000..75cd1a1fd4
--- /dev/null
+++ b/tests/test-vmstate.c
@@ -0,0 +1,357 @@
+/*
+ * Test code for VMState
+ *
+ * Copyright (c) 2013 Red Hat Inc.
+ *
+ * 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 <glib.h>
+
+#include "qemu-common.h"
+#include "migration/migration.h"
+#include "migration/vmstate.h"
+#include "block/coroutine.h"
+
+char temp_file[] = "/tmp/vmst.test.XXXXXX";
+int temp_fd;
+
+/* Fake yield_until_fd_readable() implementation so we don't have to pull the
+ * coroutine code as dependency.
+ */
+void yield_until_fd_readable(int fd)
+{
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ select(fd + 1, &fds, NULL, NULL, NULL);
+}
+
+/* Duplicate temp_fd and seek to the beginning of the file */
+static int dup_temp_fd(bool truncate)
+{
+ int fd = dup(temp_fd);
+ lseek(fd, 0, SEEK_SET);
+ if (truncate) {
+ g_assert_cmpint(ftruncate(fd, 0), ==, 0);
+ }
+ return fd;
+}
+
+typedef struct TestSruct {
+ uint32_t a, b, c, e;
+ uint64_t d, f;
+ bool skip_c_e;
+} TestStruct;
+
+
+static const VMStateDescription vmstate_simple = {
+ .name = "test",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(a, TestStruct),
+ VMSTATE_UINT32(b, TestStruct),
+ VMSTATE_UINT32(c, TestStruct),
+ VMSTATE_UINT64(d, TestStruct),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void test_simple_save(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4 };
+ vmstate_save_state(fsave, &vmstate_simple, &obj);
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ uint8_t expected[] = {
+ 0, 0, 0, 1, /* a */
+ 0, 0, 0, 2, /* b */
+ 0, 0, 0, 3, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 4, /* d */
+ };
+ uint8_t result[sizeof(expected)];
+ g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+ sizeof(result));
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+ /* Must reach EOF */
+ qemu_get_byte(loading);
+ g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+ qemu_fclose(loading);
+}
+
+static void test_simple_load(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ uint8_t buf[] = {
+ 0, 0, 0, 10, /* a */
+ 0, 0, 0, 20, /* b */
+ 0, 0, 0, 30, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 40, /* d */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+ };
+ qemu_put_buffer(fsave, buf, sizeof(buf));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ TestStruct obj;
+ vmstate_load_state(loading, &vmstate_simple, &obj, 1);
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(obj.a, ==, 10);
+ g_assert_cmpint(obj.b, ==, 20);
+ g_assert_cmpint(obj.c, ==, 30);
+ g_assert_cmpint(obj.d, ==, 40);
+ qemu_fclose(loading);
+}
+
+static const VMStateDescription vmstate_versioned = {
+ .name = "test",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(a, TestStruct),
+ VMSTATE_UINT32_V(b, TestStruct, 2), /* Versioned field in the middle, so
+ * we catch bugs more easily.
+ */
+ VMSTATE_UINT32(c, TestStruct),
+ VMSTATE_UINT64(d, TestStruct),
+ VMSTATE_UINT32_V(e, TestStruct, 2),
+ VMSTATE_UINT64_V(f, TestStruct, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void test_load_v1(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ uint8_t buf[] = {
+ 0, 0, 0, 10, /* a */
+ 0, 0, 0, 30, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 40, /* d */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+ };
+ qemu_put_buffer(fsave, buf, sizeof(buf));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ TestStruct obj = { .b = 200, .e = 500, .f = 600 };
+ vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(obj.a, ==, 10);
+ g_assert_cmpint(obj.b, ==, 200);
+ g_assert_cmpint(obj.c, ==, 30);
+ g_assert_cmpint(obj.d, ==, 40);
+ g_assert_cmpint(obj.e, ==, 500);
+ g_assert_cmpint(obj.f, ==, 600);
+ qemu_fclose(loading);
+}
+
+static void test_load_v2(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ uint8_t buf[] = {
+ 0, 0, 0, 10, /* a */
+ 0, 0, 0, 20, /* b */
+ 0, 0, 0, 30, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 40, /* d */
+ 0, 0, 0, 50, /* e */
+ 0, 0, 0, 0, 0, 0, 0, 60, /* f */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+ };
+ qemu_put_buffer(fsave, buf, sizeof(buf));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ TestStruct obj;
+ vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
+ g_assert_cmpint(obj.a, ==, 10);
+ g_assert_cmpint(obj.b, ==, 20);
+ g_assert_cmpint(obj.c, ==, 30);
+ g_assert_cmpint(obj.d, ==, 40);
+ g_assert_cmpint(obj.e, ==, 50);
+ g_assert_cmpint(obj.f, ==, 60);
+ qemu_fclose(loading);
+}
+
+static bool test_skip(void *opaque, int version_id)
+{
+ TestStruct *t = (TestStruct *)opaque;
+ return !t->skip_c_e;
+}
+
+static const VMStateDescription vmstate_skipping = {
+ .name = "test",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(a, TestStruct),
+ VMSTATE_UINT32(b, TestStruct),
+ VMSTATE_UINT32_TEST(c, TestStruct, test_skip),
+ VMSTATE_UINT64(d, TestStruct),
+ VMSTATE_UINT32_TEST(e, TestStruct, test_skip),
+ VMSTATE_UINT64_V(f, TestStruct, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void test_save_noskip(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
+ .skip_c_e = false };
+ vmstate_save_state(fsave, &vmstate_skipping, &obj);
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ uint8_t expected[] = {
+ 0, 0, 0, 1, /* a */
+ 0, 0, 0, 2, /* b */
+ 0, 0, 0, 3, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 4, /* d */
+ 0, 0, 0, 5, /* e */
+ 0, 0, 0, 0, 0, 0, 0, 6, /* f */
+ };
+ uint8_t result[sizeof(expected)];
+ g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+ sizeof(result));
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+ /* Must reach EOF */
+ qemu_get_byte(loading);
+ g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+ qemu_fclose(loading);
+}
+
+static void test_save_skip(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
+ .skip_c_e = true };
+ vmstate_save_state(fsave, &vmstate_skipping, &obj);
+ g_assert(!qemu_file_get_error(fsave));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ uint8_t expected[] = {
+ 0, 0, 0, 1, /* a */
+ 0, 0, 0, 2, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 4, /* d */
+ 0, 0, 0, 0, 0, 0, 0, 6, /* f */
+ };
+ uint8_t result[sizeof(expected)];
+ g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
+ sizeof(result));
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
+
+
+ /* Must reach EOF */
+ qemu_get_byte(loading);
+ g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
+
+ qemu_fclose(loading);
+}
+
+static void test_load_noskip(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ uint8_t buf[] = {
+ 0, 0, 0, 10, /* a */
+ 0, 0, 0, 20, /* b */
+ 0, 0, 0, 30, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 40, /* d */
+ 0, 0, 0, 50, /* e */
+ 0, 0, 0, 0, 0, 0, 0, 60, /* f */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+ };
+ qemu_put_buffer(fsave, buf, sizeof(buf));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ TestStruct obj = { .skip_c_e = false };
+ vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(obj.a, ==, 10);
+ g_assert_cmpint(obj.b, ==, 20);
+ g_assert_cmpint(obj.c, ==, 30);
+ g_assert_cmpint(obj.d, ==, 40);
+ g_assert_cmpint(obj.e, ==, 50);
+ g_assert_cmpint(obj.f, ==, 60);
+ qemu_fclose(loading);
+}
+
+static void test_load_skip(void)
+{
+ QEMUFile *fsave = qemu_fdopen(dup_temp_fd(true), "wb");
+ uint8_t buf[] = {
+ 0, 0, 0, 10, /* a */
+ 0, 0, 0, 20, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 40, /* d */
+ 0, 0, 0, 0, 0, 0, 0, 60, /* f */
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+ };
+ qemu_put_buffer(fsave, buf, sizeof(buf));
+ qemu_fclose(fsave);
+
+ QEMUFile *loading = qemu_fdopen(dup_temp_fd(false), "rb");
+ TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
+ vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+ g_assert(!qemu_file_get_error(loading));
+ g_assert_cmpint(obj.a, ==, 10);
+ g_assert_cmpint(obj.b, ==, 20);
+ g_assert_cmpint(obj.c, ==, 300);
+ g_assert_cmpint(obj.d, ==, 40);
+ g_assert_cmpint(obj.e, ==, 500);
+ g_assert_cmpint(obj.f, ==, 60);
+ qemu_fclose(loading);
+}
+
+int main(int argc, char **argv)
+{
+ temp_fd = mkstemp(temp_file);
+
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/vmstate/simple/save", test_simple_save);
+ g_test_add_func("/vmstate/simple/load", test_simple_load);
+ g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
+ g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
+ g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
+ g_test_add_func("/vmstate/field_exists/load/skip", test_load_skip);
+ g_test_add_func("/vmstate/field_exists/save/noskip", test_save_noskip);
+ g_test_add_func("/vmstate/field_exists/save/skip", test_save_skip);
+ g_test_run();
+
+ close(temp_fd);
+ unlink(temp_file);
+
+ return 0;
+}
diff --git a/trace-events b/trace-events
index 9f4456a82e..1b668d1ac2 100644
--- a/trace-events
+++ b/trace-events
@@ -402,6 +402,7 @@ usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d,
usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d"
+usb_desc_msos(int addr, int index, int len, int ret) "dev %d msos, index 0x%x, len %d, ret %d"
usb_set_addr(int addr) "dev %d"
usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"
diff --git a/translate-all.c b/translate-all.c
index aeda54dfbd..105c25aff3 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -96,12 +96,16 @@ typedef struct PageDesc {
# define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS
#endif
+/* Size of the L2 (and L3, etc) page tables. */
+#define V_L2_BITS 10
+#define V_L2_SIZE (1 << V_L2_BITS)
+
/* The bits remaining after N lower levels of page tables. */
#define V_L1_BITS_REM \
- ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS)
+ ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS)
#if V_L1_BITS_REM < 4
-#define V_L1_BITS (V_L1_BITS_REM + L2_BITS)
+#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS)
#else
#define V_L1_BITS V_L1_BITS_REM
#endif
@@ -395,18 +399,18 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1));
/* Level 2..N-1. */
- for (i = V_L1_SHIFT / L2_BITS - 1; i > 0; i--) {
+ for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) {
void **p = *lp;
if (p == NULL) {
if (!alloc) {
return NULL;
}
- ALLOC(p, sizeof(void *) * L2_SIZE);
+ ALLOC(p, sizeof(void *) * V_L2_SIZE);
*lp = p;
}
- lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1));
+ lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1));
}
pd = *lp;
@@ -414,13 +418,13 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
if (!alloc) {
return NULL;
}
- ALLOC(pd, sizeof(PageDesc) * L2_SIZE);
+ ALLOC(pd, sizeof(PageDesc) * V_L2_SIZE);
*lp = pd;
}
#undef ALLOC
- return pd + (index & (L2_SIZE - 1));
+ return pd + (index & (V_L2_SIZE - 1));
}
static inline PageDesc *page_find(tb_page_addr_t index)
@@ -655,14 +659,14 @@ static void page_flush_tb_1(int level, void **lp)
if (level == 0) {
PageDesc *pd = *lp;
- for (i = 0; i < L2_SIZE; ++i) {
+ for (i = 0; i < V_L2_SIZE; ++i) {
pd[i].first_tb = NULL;
invalidate_page_bitmap(pd + i);
}
} else {
void **pp = *lp;
- for (i = 0; i < L2_SIZE; ++i) {
+ for (i = 0; i < V_L2_SIZE; ++i) {
page_flush_tb_1(level - 1, pp + i);
}
}
@@ -673,7 +677,7 @@ static void page_flush_tb(void)
int i;
for (i = 0; i < V_L1_SIZE; i++) {
- page_flush_tb_1(V_L1_SHIFT / L2_BITS - 1, l1_map + i);
+ page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
}
}
@@ -699,11 +703,10 @@ void tb_flush(CPUArchState *env1)
CPU_FOREACH(cpu) {
CPUArchState *env = cpu->env_ptr;
- memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof(void *));
+ memset(env->tb_jmp_cache, 0, sizeof(env->tb_jmp_cache));
}
- memset(tcg_ctx.tb_ctx.tb_phys_hash, 0,
- CODE_GEN_PHYS_HASH_SIZE * sizeof(void *));
+ memset(tcg_ctx.tb_ctx.tb_phys_hash, 0, sizeof(tcg_ctx.tb_ctx.tb_phys_hash));
page_flush_tb();
tcg_ctx.code_gen_ptr = tcg_ctx.code_gen_buffer;
@@ -1600,7 +1603,7 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
if (level == 0) {
PageDesc *pd = *lp;
- for (i = 0; i < L2_SIZE; ++i) {
+ for (i = 0; i < V_L2_SIZE; ++i) {
int prot = pd[i].flags;
pa = base | (i << TARGET_PAGE_BITS);
@@ -1614,9 +1617,9 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data,
} else {
void **pp = *lp;
- for (i = 0; i < L2_SIZE; ++i) {
+ for (i = 0; i < V_L2_SIZE; ++i) {
pa = base | ((abi_ulong)i <<
- (TARGET_PAGE_BITS + L2_BITS * level));
+ (TARGET_PAGE_BITS + V_L2_BITS * level));
rc = walk_memory_regions_1(data, pa, level - 1, pp + i);
if (rc != 0) {
return rc;
@@ -1639,7 +1642,7 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
for (i = 0; i < V_L1_SIZE; i++) {
int rc = walk_memory_regions_1(&data, (abi_ulong)i << V_L1_SHIFT,
- V_L1_SHIFT / L2_BITS - 1, l1_map + i);
+ V_L1_SHIFT / V_L2_BITS - 1, l1_map + i);
if (rc != 0) {
return rc;
diff --git a/translate-all.h b/translate-all.h
index 5c38819eb8..f7e5932d65 100644
--- a/translate-all.h
+++ b/translate-all.h
@@ -19,13 +19,6 @@
#ifndef TRANSLATE_ALL_H
#define TRANSLATE_ALL_H
-/* Size of the L2 (and L3, etc) page tables. */
-#define L2_BITS 10
-#define L2_SIZE (1 << L2_BITS)
-
-#define P_L2_LEVELS \
- (((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / L2_BITS) + 1)
-
/* translate-all.c */
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
void cpu_unlink_tb(CPUState *cpu);
diff --git a/ui/cocoa.m b/ui/cocoa.m
index be491794dc..866177770a 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -52,7 +52,7 @@
#define COCOA_MOUSE_EVENT \
if (isTabletEnabled) { \
kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
- } else if (isMouseGrabed) { \
+ } else if (isMouseGrabbed) { \
kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
} else { \
[NSApp sendEvent:event]; \
@@ -129,8 +129,8 @@ int keymap[] =
14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
0, // 52 0x34 Undefined
1, // 53 0x35 0x01 ESC QZ_ESCAPE
- 0, // 54 0x36 QZ_RMETA
- 0, // 55 0x37 QZ_LMETA
+ 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA
+ 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA
42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
56, // 58 0x3A 0x38 L ALT QZ_LALT
@@ -204,10 +204,8 @@ int keymap[] =
200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
-/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
+/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
/*
- 219 // 0xdb e0,5b L GUI
- 220 // 0xdc e0,5c R GUI
221 // 0xdd e0,5d APPS
// E0,2A,E0,37 PRNT SCRN
// E1,1D,45,E1,9D,C5 PAUSE
@@ -240,9 +238,8 @@ int keymap[] =
static int cocoa_keycode_to_qemu(int keycode)
{
- if((sizeof(keymap)/sizeof(int)) <= keycode)
- {
- printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
+ if (ARRAY_SIZE(keymap) <= keycode) {
+ fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode);
return 0;
}
return keymap[keycode];
@@ -262,7 +259,7 @@ static int cocoa_keycode_to_qemu(int keycode)
float cx,cy,cw,ch,cdx,cdy;
CGDataProviderRef dataProviderRef;
int modifiers_state[256];
- BOOL isMouseGrabed;
+ BOOL isMouseGrabbed;
BOOL isFullscreen;
BOOL isAbsoluteEnabled;
BOOL isTabletEnabled;
@@ -273,7 +270,7 @@ static int cocoa_keycode_to_qemu(int keycode)
- (void) toggleFullScreen:(id)sender;
- (void) handleEvent:(NSEvent *)event;
- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
-- (BOOL) isMouseGrabed;
+- (BOOL) isMouseGrabbed;
- (BOOL) isAbsoluteEnabled;
- (float) cdx;
- (float) cdy;
@@ -324,7 +321,12 @@ QemuCocoaView *cocoaView;
CGContextSetShouldAntialias (viewContextRef, NO);
// draw screen bitmap directly to Core Graphics context
- if (dataProviderRef) {
+ if (!dataProviderRef) {
+ // Draw request before any guest device has set up a framebuffer:
+ // just draw an opaque black rectangle
+ CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0);
+ CGContextFillRect(viewContextRef, NSRectToCGRect(rect));
+ } else {
CGImageRef imageRef = CGImageCreate(
screen.width, //width
screen.height, //height
@@ -408,31 +410,41 @@ QemuCocoaView *cocoaView;
int w = surface_width(surface);
int h = surface_height(surface);
+ bool isResize = (w != screen.width || h != screen.height);
+
+ int oldh = screen.height;
+ if (isResize) {
+ // Resize before we trigger the redraw, or we'll redraw at the wrong size
+ COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h);
+ screen.width = w;
+ screen.height = h;
+ [self setContentDimensions];
+ [self setFrame:NSMakeRect(cx, cy, cw, ch)];
+ }
// update screenBuffer
if (dataProviderRef)
CGDataProviderRelease(dataProviderRef);
//sync host window color space with guests
- screen.bitsPerPixel = surface_bits_per_pixel(surface);
- screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2;
+ screen.bitsPerPixel = surface_bits_per_pixel(surface);
+ screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2;
dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL);
// update windows
if (isFullscreen) {
[[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
- [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO];
} else {
if (qemu_name)
[normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
- [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO];
+ }
+
+ if (isResize) {
+ [normalWindow center];
}
- screen.width = w;
- screen.height = h;
- [normalWindow center];
- [self setContentDimensions];
- [self setFrame:NSMakeRect(cx, cy, cw, ch)];
}
- (void) toggleFullScreen:(id)sender
@@ -494,6 +506,12 @@ QemuCocoaView *cocoaView;
switch ([event type]) {
case NSFlagsChanged:
keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) {
+ /* Don't pass command key changes to guest unless mouse is grabbed */
+ keycode = 0;
+ }
+
if (keycode) {
if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
kbd_put_keycode(keycode);
@@ -517,15 +535,15 @@ QemuCocoaView *cocoaView;
}
break;
case NSKeyDown:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
- // forward command Key Combos
- if ([event modifierFlags] & NSCommandKeyMask) {
+ // forward command key combos to the host UI unless the mouse is grabbed
+ if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
[NSApp sendEvent:event];
return;
}
// default
- keycode = cocoa_keycode_to_qemu([event keyCode]);
// handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
@@ -581,6 +599,13 @@ QemuCocoaView *cocoaView;
break;
case NSKeyUp:
keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ // don't pass the guest a spurious key-up if we treated this
+ // command-key combo as a host UI action
+ if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
+ return;
+ }
+
if (qemu_console_is_graphic(NULL)) {
if (keycode & 0x80)
kbd_put_keycode(0xe0);
@@ -638,7 +663,7 @@ QemuCocoaView *cocoaView;
case NSLeftMouseUp:
if (isTabletEnabled) {
COCOA_MOUSE_EVENT
- } else if (!isMouseGrabed) {
+ } else if (!isMouseGrabbed) {
if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
[self grabMouse];
} else {
@@ -655,7 +680,7 @@ QemuCocoaView *cocoaView;
COCOA_MOUSE_EVENT
break;
case NSScrollWheel:
- if (isTabletEnabled || isMouseGrabed) {
+ if (isTabletEnabled || isMouseGrabbed) {
kbd_mouse_event(0, 0, -[event deltaY], 0);
} else {
[NSApp sendEvent:event];
@@ -678,7 +703,7 @@ QemuCocoaView *cocoaView;
}
[NSCursor hide];
CGAssociateMouseAndMouseCursorPosition(FALSE);
- isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
+ isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
}
- (void) ungrabMouse
@@ -693,11 +718,11 @@ QemuCocoaView *cocoaView;
}
[NSCursor unhide];
CGAssociateMouseAndMouseCursorPosition(TRUE);
- isMouseGrabed = FALSE;
+ isMouseGrabbed = FALSE;
}
- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
-- (BOOL) isMouseGrabed {return isMouseGrabed;}
+- (BOOL) isMouseGrabbed {return isMouseGrabbed;}
- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
- (float) cdx {return cdx;}
- (float) cdy {return cdy;}
@@ -749,7 +774,7 @@ QemuCocoaView *cocoaView;
[normalWindow setContentView:cocoaView];
[normalWindow useOptimizedDrawing:YES];
[normalWindow makeKeyAndOrderFront:self];
- [normalWindow center];
+ [normalWindow center];
}
return self;
@@ -768,14 +793,14 @@ QemuCocoaView *cocoaView;
{
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
- // Display an open dialog box if no argument were passed or
+ // Display an open dialog box if no arguments were passed or
// if qemu was launched from the finder ( the Finder passes "-psn" )
if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
NSOpenPanel *op = [[NSOpenPanel alloc] init];
[op setPrompt:@"Boot image"];
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
NSArray *filetypes = [NSArray arrayWithObjects:@"img", @"iso", @"dmg",
- @"qcow", @"cow", @"cloop", @"vmdk", nil];
+ @"qcow", @"qcow2", @"cow", @"cloop", @"vmdk", nil];
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
[op setAllowedFileTypes:filetypes];
[op beginSheetModalForWindow:normalWindow
@@ -823,18 +848,18 @@ QemuCocoaView *cocoaView;
if(returnCode == NSCancelButton) {
exit(0);
} else if(returnCode == NSOKButton) {
- const char *bin = "qemu";
char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding];
- char **argv = (char**)malloc( sizeof(char*)*3 );
+ char **argv = g_new(char *, 4);
[sheet close];
- argv[0] = g_strdup_printf("%s", bin);
- argv[1] = g_strdup_printf("-hda");
- argv[2] = g_strdup_printf("%s", img);
+ argv[0] = g_strdup(gArgv[0]);
+ argv[1] = g_strdup("-hda");
+ argv[2] = g_strdup(img);
+ argv[3] = NULL;
- printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
+ // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img);
[self startEmulationWithArgc:3 argv:(char**)argv];
}
@@ -1000,7 +1025,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
if (kbd_mouse_is_absolute()) {
if (![cocoaView isAbsoluteEnabled]) {
- if ([cocoaView isMouseGrabed]) {
+ if ([cocoaView isMouseGrabbed]) {
[cocoaView ungrabMouse];
}
}
diff --git a/ui/gtk.c b/ui/gtk.c
index 6316f5ba00..a633d89346 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -34,6 +34,10 @@
#define GETTEXT_PACKAGE "qemu"
#define LOCALEDIR "po"
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */
+#endif
+
#include "qemu-common.h"
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
@@ -704,11 +708,18 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
GtkDisplayState *s = opaque;
- int gdk_keycode;
- int qemu_keycode;
+ int gdk_keycode = key->hardware_keycode;
int i;
- gdk_keycode = key->hardware_keycode;
+#ifdef _WIN32
+ UINT qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
+ switch (qemu_keycode) {
+ case 103: /* alt gr */
+ qemu_keycode = 56 | SCANCODE_GREY;
+ break;
+ }
+#else
+ int qemu_keycode;
if (gdk_keycode < 9) {
qemu_keycode = 0;
@@ -723,6 +734,7 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
} else {
qemu_keycode = 0;
}
+#endif
trace_gd_key_event(gdk_keycode, qemu_keycode,
(key->type == GDK_KEY_PRESS) ? "down" : "up");
diff --git a/ui/spice-core.c b/ui/spice-core.c
index e4d533d4c4..4cce3b38c0 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -47,6 +47,7 @@ static const char *auth = "spice";
static char *auth_passwd;
static time_t auth_expires = TIME_MAX;
static int spice_migration_completed;
+static int spice_display_is_running;
int using_spice = 0;
static QemuThread me;
@@ -622,9 +623,7 @@ static void vm_change_state_handler(void *opaque, int running,
{
if (running) {
qemu_spice_display_start();
- spice_server_vm_start(spice_server);
} else {
- spice_server_vm_stop(spice_server);
qemu_spice_display_stop();
}
}
@@ -776,6 +775,8 @@ void qemu_spice_init(void)
if (str) {
int streaming_video = parse_stream_video(str);
spice_server_set_streaming_video(spice_server, streaming_video);
+ } else {
+ spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
}
spice_server_set_agent_mouse
@@ -902,6 +903,23 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls)
}
}
+void qemu_spice_display_start(void)
+{
+ spice_display_is_running = true;
+ spice_server_vm_start(spice_server);
+}
+
+void qemu_spice_display_stop(void)
+{
+ spice_server_vm_stop(spice_server);
+ spice_display_is_running = false;
+}
+
+int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
+{
+ return spice_display_is_running;
+}
+
static void spice_register_config(void)
{
qemu_add_opts(&qemu_spice_opts);
diff --git a/ui/spice-display.c b/ui/spice-display.c
index f23a31854d..9bb42f1461 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -126,23 +126,6 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
spice_qxl_wakeup(&ssd->qxl);
}
-static int spice_display_is_running;
-
-void qemu_spice_display_start(void)
-{
- spice_display_is_running = true;
-}
-
-void qemu_spice_display_stop(void)
-{
- spice_display_is_running = false;
-}
-
-int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
-{
- return spice_display_is_running;
-}
-
static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
QXLRect *rect)
{
diff --git a/util/Makefile.objs b/util/Makefile.objs
index af3e5cb157..937376b082 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -13,3 +13,4 @@ util-obj-y += hexdump.o
util-obj-y += crc32c.o
util-obj-y += throttle.o
util-obj-y += getauxval.o
+util-obj-y += readline.o
diff --git a/util/bitmap.c b/util/bitmap.c
index 687841dcec..9c6bb526f6 100644
--- a/util/bitmap.c
+++ b/util/bitmap.c
@@ -36,9 +36,9 @@
* endian architectures.
*/
-int slow_bitmap_empty(const unsigned long *bitmap, int bits)
+int slow_bitmap_empty(const unsigned long *bitmap, long bits)
{
- int k, lim = bits/BITS_PER_LONG;
+ long k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap[k]) {
@@ -54,9 +54,9 @@ int slow_bitmap_empty(const unsigned long *bitmap, int bits)
return 1;
}
-int slow_bitmap_full(const unsigned long *bitmap, int bits)
+int slow_bitmap_full(const unsigned long *bitmap, long bits)
{
- int k, lim = bits/BITS_PER_LONG;
+ long k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (~bitmap[k]) {
@@ -74,9 +74,9 @@ int slow_bitmap_full(const unsigned long *bitmap, int bits)
}
int slow_bitmap_equal(const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k, lim = bits/BITS_PER_LONG;
+ long k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap1[k] != bitmap2[k]) {
@@ -94,9 +94,9 @@ int slow_bitmap_equal(const unsigned long *bitmap1,
}
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
- int bits)
+ long bits)
{
- int k, lim = bits/BITS_PER_LONG;
+ long k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
dst[k] = ~src[k];
@@ -108,10 +108,10 @@ void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
}
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k;
- int nr = BITS_TO_LONGS(bits);
+ long k;
+ long nr = BITS_TO_LONGS(bits);
unsigned long result = 0;
for (k = 0; k < nr; k++) {
@@ -121,10 +121,10 @@ int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
}
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k;
- int nr = BITS_TO_LONGS(bits);
+ long k;
+ long nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++) {
dst[k] = bitmap1[k] | bitmap2[k];
@@ -132,10 +132,10 @@ void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
}
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k;
- int nr = BITS_TO_LONGS(bits);
+ long k;
+ long nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++) {
dst[k] = bitmap1[k] ^ bitmap2[k];
@@ -143,10 +143,10 @@ void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
}
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k;
- int nr = BITS_TO_LONGS(bits);
+ long k;
+ long nr = BITS_TO_LONGS(bits);
unsigned long result = 0;
for (k = 0; k < nr; k++) {
@@ -157,10 +157,10 @@ int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
-void bitmap_set(unsigned long *map, int start, int nr)
+void bitmap_set(unsigned long *map, long start, long nr)
{
unsigned long *p = map + BIT_WORD(start);
- const int size = start + nr;
+ 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);
@@ -177,10 +177,10 @@ void bitmap_set(unsigned long *map, int start, int nr)
}
}
-void bitmap_clear(unsigned long *map, int start, int nr)
+void bitmap_clear(unsigned long *map, long start, long nr)
{
unsigned long *p = map + BIT_WORD(start);
- const int size = start + nr;
+ 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);
@@ -212,10 +212,10 @@ void bitmap_clear(unsigned long *map, int start, int nr)
* power of 2. A @align_mask of 0 means no alignment is required.
*/
unsigned long bitmap_find_next_zero_area(unsigned long *map,
- unsigned long size,
- unsigned long start,
- unsigned int nr,
- unsigned long align_mask)
+ unsigned long size,
+ unsigned long start,
+ unsigned long nr,
+ unsigned long align_mask)
{
unsigned long index, end, i;
again:
@@ -237,9 +237,9 @@ again:
}
int slow_bitmap_intersects(const unsigned long *bitmap1,
- const unsigned long *bitmap2, int bits)
+ const unsigned long *bitmap2, long bits)
{
- int k, lim = bits/BITS_PER_LONG;
+ long k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap1[k] & bitmap2[k]) {
diff --git a/util/error.c b/util/error.c
index 3ee362a7f5..f11f1d57a0 100644
--- a/util/error.c
+++ b/util/error.c
@@ -23,6 +23,8 @@ struct Error
ErrorClass err_class;
};
+Error *error_abort;
+
void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...)
{
Error *err;
@@ -41,6 +43,11 @@ void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...)
va_end(ap);
err->err_class = err_class;
+ if (errp == &error_abort) {
+ error_report("%s", error_get_pretty(err));
+ abort();
+ }
+
*errp = err;
errno = saved_errno;
@@ -72,6 +79,11 @@ void error_set_errno(Error **errp, int os_errno, ErrorClass err_class,
va_end(ap);
err->err_class = err_class;
+ if (errp == &error_abort) {
+ error_report("%s", error_get_pretty(err));
+ abort();
+ }
+
*errp = err;
errno = saved_errno;
@@ -112,6 +124,11 @@ void error_set_win32(Error **errp, int win32_err, ErrorClass err_class,
va_end(ap);
err->err_class = err_class;
+ if (errp == &error_abort) {
+ error_report("%s", error_get_pretty(err));
+ abort();
+ }
+
*errp = err;
}
@@ -153,7 +170,10 @@ void error_free(Error *err)
void error_propagate(Error **dst_err, Error *local_err)
{
- if (dst_err && !*dst_err) {
+ if (local_err && dst_err == &error_abort) {
+ error_report("%s", error_get_pretty(local_err));
+ abort();
+ } else if (dst_err && !*dst_err) {
*dst_err = local_err;
} else if (local_err) {
error_free(local_err);
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index e00a44c86f..d5dca4729a 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -47,6 +47,9 @@ extern int daemon(int, int);
# define QEMU_VMALLOC_ALIGN getpagesize()
#endif
+#include <termios.h>
+#include <unistd.h>
+
#include <glib/gprintf.h>
#include "config-host.h"
@@ -85,6 +88,11 @@ void *qemu_oom_check(void *ptr)
void *qemu_memalign(size_t alignment, size_t size)
{
void *ptr;
+
+ if (alignment < sizeof(void*)) {
+ alignment = sizeof(void*);
+ }
+
#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
int ret;
ret = posix_memalign(&ptr, alignment, size);
@@ -251,3 +259,18 @@ qemu_get_local_state_pathname(const char *relative_pathname)
return g_strdup_printf("%s/%s", CONFIG_QEMU_LOCALSTATEDIR,
relative_pathname);
}
+
+void qemu_set_tty_echo(int fd, bool echo)
+{
+ struct termios tty;
+
+ tcgetattr(fd, &tty);
+
+ if (echo) {
+ tty.c_lflag |= ECHO | ECHONL | ICANON | IEXTEN;
+ } else {
+ tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
+ }
+
+ tcsetattr(fd, TCSANOW, &tty);
+}
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 776ccfaaf0..50be0440f2 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -189,3 +189,22 @@ qemu_get_local_state_pathname(const char *relative_pathname)
return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", base_path,
relative_pathname);
}
+
+void qemu_set_tty_echo(int fd, bool echo)
+{
+ HANDLE handle = (HANDLE)_get_osfhandle(fd);
+ DWORD dwMode = 0;
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ return;
+ }
+
+ GetConsoleMode(handle, &dwMode);
+
+ if (echo) {
+ SetConsoleMode(handle, dwMode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
+ } else {
+ SetConsoleMode(handle,
+ dwMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT));
+ }
+}
diff --git a/util/qemu-config.c b/util/qemu-config.c
index 04da942a25..9298f55ecf 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -311,7 +311,7 @@ int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
error_free(local_err);
goto out;
}
- opts = qemu_opts_create_nofail(list);
+ opts = qemu_opts_create(list, NULL, 0, &error_abort);
continue;
}
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
@@ -356,3 +356,103 @@ int qemu_read_config_file(const char *filename)
return -EINVAL;
}
}
+
+static void config_parse_qdict_section(QDict *options, QemuOptsList *opts,
+ Error **errp)
+{
+ QemuOpts *subopts;
+ QDict *subqdict;
+ QList *list = NULL;
+ Error *local_err = NULL;
+ size_t orig_size, enum_size;
+ char *prefix;
+
+ prefix = g_strdup_printf("%s.", opts->name);
+ qdict_extract_subqdict(options, &subqdict, prefix);
+ g_free(prefix);
+ orig_size = qdict_size(subqdict);
+ if (!orig_size) {
+ goto out;
+ }
+
+ subopts = qemu_opts_create(opts, NULL, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ qemu_opts_absorb_qdict(subopts, subqdict, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ enum_size = qdict_size(subqdict);
+ if (enum_size < orig_size && enum_size) {
+ error_setg(errp, "Unknown option '%s' for [%s]",
+ qdict_first(subqdict)->key, opts->name);
+ goto out;
+ }
+
+ if (enum_size) {
+ /* Multiple, enumerated sections */
+ QListEntry *list_entry;
+ unsigned i = 0;
+
+ /* Not required anymore */
+ qemu_opts_del(subopts);
+
+ qdict_array_split(subqdict, &list);
+ if (qdict_size(subqdict)) {
+ error_setg(errp, "Unused option '%s' for [%s]",
+ qdict_first(subqdict)->key, opts->name);
+ goto out;
+ }
+
+ QLIST_FOREACH_ENTRY(list, list_entry) {
+ QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry));
+ char *opt_name;
+
+ opt_name = g_strdup_printf("%s.%u", opts->name, i++);
+ subopts = qemu_opts_create(opts, opt_name, 1, &local_err);
+ g_free(opt_name);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ qemu_opts_absorb_qdict(subopts, section, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ qemu_opts_del(subopts);
+ goto out;
+ }
+
+ if (qdict_size(section)) {
+ error_setg(errp, "[%s] section doesn't support the option '%s'",
+ opts->name, qdict_first(section)->key);
+ qemu_opts_del(subopts);
+ goto out;
+ }
+ }
+ }
+
+out:
+ QDECREF(subqdict);
+ QDECREF(list);
+}
+
+void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists,
+ Error **errp)
+{
+ int i;
+ Error *local_err = NULL;
+
+ for (i = 0; lists[i]; i++) {
+ config_parse_qdict_section(options, lists[i], &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+}
diff --git a/util/qemu-option.c b/util/qemu-option.c
index efcb5dcfcb..668e5d919f 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -791,15 +791,6 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
return opts;
}
-QemuOpts *qemu_opts_create_nofail(QemuOptsList *list)
-{
- QemuOpts *opts;
- Error *errp = NULL;
- opts = qemu_opts_create(list, NULL, 0, &errp);
- assert_no_error(errp);
- return opts;
-}
-
void qemu_opts_reset(QemuOptsList *list)
{
QemuOpts *opts, *next_opts;
diff --git a/util/qemu-progress.c b/util/qemu-progress.c
index 9a3f96cd47..4ee5cd07f2 100644
--- a/util/qemu-progress.c
+++ b/util/qemu-progress.c
@@ -24,7 +24,6 @@
#include "qemu-common.h"
#include "qemu/osdep.h"
-#include "sysemu/sysemu.h"
#include <stdio.h>
struct progress_state {
@@ -83,12 +82,22 @@ static void progress_dummy_init(void)
{
#ifdef CONFIG_POSIX
struct sigaction action;
+ sigset_t set;
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask);
action.sa_handler = sigusr_print;
action.sa_flags = 0;
sigaction(SIGUSR1, &action, NULL);
+
+ /*
+ * SIGUSR1 is SIG_IPI and gets blocked in qemu_init_main_loop(). In the
+ * tools that use the progress report SIGUSR1 isn't used in this meaning
+ * and instead should print the progress, so reenable it.
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
#endif
state.print = progress_dummy_print;
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 6b97dc11f9..8818d7c0de 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -578,7 +578,7 @@ int inet_listen(const char *str, char *ostr, int olen,
addr = inet_parse(str, errp);
if (addr != NULL) {
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_listen_opts(opts, port_offset, errp);
@@ -617,7 +617,7 @@ int inet_connect(const char *str, Error **errp)
addr = inet_parse(str, errp);
if (addr != NULL) {
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, NULL, NULL);
@@ -651,7 +651,7 @@ int inet_nonblocking_connect(const char *str,
addr = inet_parse(str, errp);
if (addr != NULL) {
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, callback, opaque);
@@ -794,7 +794,7 @@ int unix_listen(const char *str, char *ostr, int olen, Error **errp)
char *path, *optstr;
int sock, len;
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
optstr = strchr(str, ',');
if (optstr) {
@@ -822,7 +822,7 @@ int unix_connect(const char *path, Error **errp)
QemuOpts *opts;
int sock;
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts, errp, NULL, NULL);
qemu_opts_del(opts);
@@ -839,7 +839,7 @@ int unix_nonblocking_connect(const char *path,
g_assert(callback != NULL);
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts, errp, callback, opaque);
qemu_opts_del(opts);
@@ -889,7 +889,7 @@ int socket_connect(SocketAddress *addr, Error **errp,
QemuOpts *opts;
int fd;
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet);
@@ -921,7 +921,7 @@ int socket_listen(SocketAddress *addr, Error **errp)
QemuOpts *opts;
int fd;
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet);
@@ -949,7 +949,7 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
QemuOpts *opts;
int fd;
- opts = qemu_opts_create_nofail(&socket_optslist);
+ opts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
switch (remote->kind) {
case SOCKET_ADDRESS_KIND_INET:
qemu_opt_set(opts, "host", remote->inet->host);
diff --git a/readline.c b/util/readline.c
index abf27ddec3..8441be484c 100644
--- a/readline.c
+++ b/util/readline.c
@@ -21,21 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "monitor/readline.h"
-#include "monitor/monitor.h"
+
+#include "qemu-common.h"
+#include "qemu/readline.h"
#define IS_NORM 0
#define IS_ESC 1
#define IS_CSI 2
#define IS_SS3 3
-#undef printf
-#define printf do_not_use_printf
-
void readline_show_prompt(ReadLineState *rs)
{
- monitor_printf(rs->mon, "%s", rs->prompt);
- monitor_flush(rs->mon);
+ rs->printf_func(rs->opaque, "%s", rs->prompt);
+ rs->flush_func(rs->opaque);
rs->last_cmd_buf_index = 0;
rs->last_cmd_buf_size = 0;
rs->esc_state = IS_NORM;
@@ -49,17 +47,17 @@ static void readline_update(ReadLineState *rs)
if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
for(i = 0; i < rs->last_cmd_buf_index; i++) {
- monitor_printf(rs->mon, "\033[D");
+ rs->printf_func(rs->opaque, "\033[D");
}
rs->cmd_buf[rs->cmd_buf_size] = '\0';
if (rs->read_password) {
len = strlen(rs->cmd_buf);
for(i = 0; i < len; i++)
- monitor_printf(rs->mon, "*");
+ rs->printf_func(rs->opaque, "*");
} else {
- monitor_printf(rs->mon, "%s", rs->cmd_buf);
+ rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
}
- monitor_printf(rs->mon, "\033[K");
+ rs->printf_func(rs->opaque, "\033[K");
memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
rs->last_cmd_buf_size = rs->cmd_buf_size;
rs->last_cmd_buf_index = rs->cmd_buf_size;
@@ -68,17 +66,17 @@ static void readline_update(ReadLineState *rs)
delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
if (delta > 0) {
for(i = 0;i < delta; i++) {
- monitor_printf(rs->mon, "\033[C");
+ rs->printf_func(rs->opaque, "\033[C");
}
} else {
delta = -delta;
for(i = 0;i < delta; i++) {
- monitor_printf(rs->mon, "\033[D");
+ rs->printf_func(rs->opaque, "\033[D");
}
}
rs->last_cmd_buf_index = rs->cmd_buf_index;
}
- monitor_flush(rs->mon);
+ rs->flush_func(rs->opaque);
}
static void readline_insert_char(ReadLineState *rs, int ch)
@@ -284,7 +282,7 @@ static void readline_completion(ReadLineState *rs)
cmdline = g_malloc(rs->cmd_buf_index + 1);
memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
cmdline[rs->cmd_buf_index] = '\0';
- rs->completion_finder(rs->mon, cmdline);
+ rs->completion_finder(rs->opaque, cmdline);
g_free(cmdline);
/* no completion found */
@@ -299,7 +297,7 @@ static void readline_completion(ReadLineState *rs)
if (len > 0 && rs->completions[0][len - 1] != '/')
readline_insert_char(rs, ' ');
} else {
- monitor_printf(rs->mon, "\n");
+ rs->printf_func(rs->opaque, "\n");
max_width = 0;
max_prefix = 0;
for(i = 0; i < rs->nb_completions; i++) {
@@ -329,9 +327,9 @@ static void readline_completion(ReadLineState *rs)
nb_cols = 80 / max_width;
j = 0;
for(i = 0; i < rs->nb_completions; i++) {
- monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
+ rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
if (++j == nb_cols || i == (rs->nb_completions - 1)) {
- monitor_printf(rs->mon, "\n");
+ rs->printf_func(rs->opaque, "\n");
j = 0;
}
}
@@ -365,12 +363,12 @@ void readline_handle_byte(ReadLineState *rs, int ch)
rs->cmd_buf[rs->cmd_buf_size] = '\0';
if (!rs->read_password)
readline_hist_add(rs, rs->cmd_buf);
- monitor_printf(rs->mon, "\n");
+ rs->printf_func(rs->opaque, "\n");
rs->cmd_buf_index = 0;
rs->cmd_buf_size = 0;
rs->last_cmd_buf_index = 0;
rs->last_cmd_buf_size = 0;
- rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
+ rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque);
break;
case 23:
/* ^W */
@@ -480,13 +478,17 @@ const char *readline_get_history(ReadLineState *rs, unsigned int index)
return rs->history[index];
}
-ReadLineState *readline_init(Monitor *mon,
+ReadLineState *readline_init(ReadLinePrintfFunc *printf_func,
+ ReadLineFlushFunc *flush_func,
+ void *opaque,
ReadLineCompletionFunc *completion_finder)
{
ReadLineState *rs = g_malloc0(sizeof(*rs));
rs->hist_entry = -1;
- rs->mon = mon;
+ rs->opaque = opaque;
+ rs->printf_func = printf_func;
+ rs->flush_func = flush_func;
rs->completion_finder = completion_finder;
return rs;
diff --git a/vl.c b/vl.c
index b0399de25f..7f4fe0d5df 100644
--- a/vl.c
+++ b/vl.c
@@ -230,7 +230,7 @@ int ctrl_grab = 0;
unsigned int nb_prom_envs = 0;
const char *prom_envs[MAX_PROM_ENVS];
int boot_menu;
-bool boot_strict;
+static bool boot_strict;
uint8_t *boot_splash_filedata;
size_t boot_splash_filedata_size;
uint8_t qemu_extra_params_fw[2];
@@ -461,7 +461,7 @@ static QemuOptsList qemu_boot_opts = {
.type = QEMU_OPT_STRING,
}, {
.name = "strict",
- .type = QEMU_OPT_STRING,
+ .type = QEMU_OPT_BOOL,
},
{ /*End of list */ }
},
@@ -545,7 +545,7 @@ QemuOpts *qemu_get_machine_opts(void)
assert(list);
opts = qemu_opts_find(list, NULL);
if (!opts) {
- opts = qemu_opts_create_nofail(list);
+ opts = qemu_opts_create(list, NULL, 0, &error_abort);
}
return opts;
}
@@ -591,6 +591,7 @@ typedef struct {
static const RunStateTransition runstate_transitions_def[] = {
/* from -> to */
{ RUN_STATE_DEBUG, RUN_STATE_RUNNING },
+ { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE },
{ RUN_STATE_INMIGRATE, RUN_STATE_RUNNING },
{ RUN_STATE_INMIGRATE, RUN_STATE_PAUSED },
@@ -2254,7 +2255,8 @@ static int balloon_parse(const char *arg)
return -1;
} else {
/* create empty opts */
- opts = qemu_opts_create_nofail(qemu_find_opts("device"));
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ &error_abort);
}
qemu_opt_set(opts, "driver", "virtio-balloon");
return 0;
@@ -2514,14 +2516,14 @@ static int virtcon_parse(const char *devname)
exit(1);
}
- bus_opts = qemu_opts_create_nofail(device);
+ bus_opts = qemu_opts_create(device, NULL, 0, &error_abort);
if (arch_type == QEMU_ARCH_S390X) {
qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
} else {
qemu_opt_set(bus_opts, "driver", "virtio-serial-pci");
}
- dev_opts = qemu_opts_create_nofail(device);
+ dev_opts = qemu_opts_create(device, NULL, 0, &error_abort);
qemu_opt_set(dev_opts, "driver", "virtconsole");
snprintf(label, sizeof(label), "virtcon%d", index);
@@ -2624,7 +2626,7 @@ static struct {
{ "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
{ "xen", "Xen", xen_available, xen_init, &xen_allowed },
{ "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },
- { "qtest", "QTest", qtest_available, qtest_init, &qtest_allowed },
+ { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed },
};
static int configure_accelerator(void)
@@ -2809,12 +2811,13 @@ static int object_create(QemuOpts *opts, void *opaque)
obj = object_new(type);
if (qemu_opt_foreach(opts, object_set_property, obj, 1) < 0) {
+ object_unref(obj);
return -1;
}
object_property_add_child(container_get(object_get_root(), "/objects"),
id, obj, NULL);
-
+ object_unref(obj);
return 0;
}
@@ -2836,6 +2839,8 @@ int main(int argc, char **argv, char **envp)
QEMUMachine *machine;
const char *cpu_model;
const char *vga_model = "none";
+ const char *qtest_chrdev = NULL;
+ const char *qtest_log = NULL;
const char *pid_file = NULL;
const char *incoming = NULL;
#ifdef CONFIG_VNC
@@ -3378,7 +3383,8 @@ int main(int argc, char **argv, char **envp)
qemu_opt_set_bool(fsdev, "readonly",
qemu_opt_get_bool(opts, "readonly", 0));
- device = qemu_opts_create_nofail(qemu_find_opts("device"));
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ &error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev",
qemu_opt_get(opts, "mount_tag"));
@@ -3398,7 +3404,8 @@ int main(int argc, char **argv, char **envp)
}
qemu_opt_set(fsdev, "fsdriver", "synth");
- device = qemu_opts_create_nofail(qemu_find_opts("device"));
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ &error_abort);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev", "v_synth");
qemu_opt_set(device, "mount_tag", "v_synth");
@@ -3489,11 +3496,16 @@ int main(int argc, char **argv, char **envp)
}
case QEMU_OPTION_acpitable:
opts = qemu_opts_parse(qemu_find_opts("acpi"), optarg, 1);
- g_assert(opts != NULL);
+ if (!opts) {
+ exit(1);
+ }
do_acpitable_option(opts);
break;
case QEMU_OPTION_smbios:
opts = qemu_opts_parse(qemu_find_opts("smbios"), optarg, 0);
+ if (!opts) {
+ exit(1);
+ }
do_smbios_option(opts);
break;
case QEMU_OPTION_enable_kvm:
@@ -4043,8 +4055,8 @@ int main(int argc, char **argv, char **envp)
configure_accelerator();
- if (!qtest_enabled() && qtest_chrdev) {
- qtest_init();
+ if (qtest_chrdev) {
+ qtest_init(qtest_chrdev, qtest_log);
}
machine_opts = qemu_get_machine_opts();
@@ -4074,6 +4086,7 @@ int main(int argc, char **argv, char **envp)
}
boot_menu = qemu_opt_get_bool(opts, "menu", boot_menu);
+ boot_strict = qemu_opt_get_bool(opts, "strict", false);
}
if (!kernel_cmdline) {
@@ -4239,7 +4252,8 @@ int main(int argc, char **argv, char **envp)
qdev_machine_init();
- QEMUMachineInitArgs args = { .ram_size = ram_size,
+ QEMUMachineInitArgs args = { .machine = machine,
+ .ram_size = ram_size,
.boot_order = boot_order,
.kernel_filename = kernel_filename,
.kernel_cmdline = kernel_cmdline,
diff --git a/vmstate.c b/vmstate.c
new file mode 100644
index 0000000000..284b080f46
--- /dev/null
+++ b/vmstate.c
@@ -0,0 +1,650 @@
+#include "qemu-common.h"
+#include "migration/migration.h"
+#include "migration/qemu-file.h"
+#include "migration/vmstate.h"
+#include "qemu/bitops.h"
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+
+int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id)
+{
+ VMStateField *field = vmsd->fields;
+ int ret;
+
+ if (version_id > vmsd->version_id) {
+ return -EINVAL;
+ }
+ if (version_id < vmsd->minimum_version_id_old) {
+ return -EINVAL;
+ }
+ if (version_id < vmsd->minimum_version_id) {
+ return vmsd->load_state_old(f, opaque, version_id);
+ }
+ if (vmsd->pre_load) {
+ int ret = vmsd->pre_load(opaque);
+ if (ret) {
+ return ret;
+ }
+ }
+ while (field->name) {
+ if ((field->field_exists &&
+ field->field_exists(opaque, version_id)) ||
+ (!field->field_exists &&
+ field->version_id <= version_id)) {
+ void *base_addr = opaque + field->offset;
+ int i, n_elems = 1;
+ int size = field->size;
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque+field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT32) {
+ n_elems = *(uint32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT8) {
+ n_elems = *(uint8_t *)(opaque+field->num_offset);
+ }
+ if (field->flags & VMS_POINTER) {
+ base_addr = *(void **)base_addr + field->start;
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *addr = base_addr + size * i;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ addr = *(void **)addr;
+ }
+ if (field->flags & VMS_STRUCT) {
+ ret = vmstate_load_state(f, field->vmsd, addr,
+ field->vmsd->version_id);
+ } else {
+ ret = field->info->get(f, addr, size);
+
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+ field++;
+ }
+ ret = vmstate_subsection_load(f, vmsd, opaque);
+ if (ret != 0) {
+ return ret;
+ }
+ if (vmsd->post_load) {
+ return vmsd->post_load(opaque, version_id);
+ }
+ return 0;
+}
+
+void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ VMStateField *field = vmsd->fields;
+
+ if (vmsd->pre_save) {
+ vmsd->pre_save(opaque);
+ }
+ while (field->name) {
+ if (!field->field_exists ||
+ field->field_exists(opaque, vmsd->version_id)) {
+ void *base_addr = opaque + field->offset;
+ int i, n_elems = 1;
+ int size = field->size;
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque+field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT32) {
+ n_elems = *(uint32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT8) {
+ n_elems = *(uint8_t *)(opaque+field->num_offset);
+ }
+ if (field->flags & VMS_POINTER) {
+ base_addr = *(void **)base_addr + field->start;
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *addr = base_addr + size * i;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ addr = *(void **)addr;
+ }
+ if (field->flags & VMS_STRUCT) {
+ vmstate_save_state(f, field->vmsd, addr);
+ } else {
+ field->info->put(f, addr, size);
+ }
+ }
+ }
+ field++;
+ }
+ vmstate_subsection_save(f, vmsd, opaque);
+}
+
+static const VMStateDescription *
+ vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+{
+ while (sub && sub->needed) {
+ if (strcmp(idstr, sub->vmsd->name) == 0) {
+ return sub->vmsd;
+ }
+ sub++;
+ }
+ return NULL;
+}
+
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
+ char idstr[256];
+ int ret;
+ uint8_t version_id, len, size;
+ const VMStateDescription *sub_vmsd;
+
+ len = qemu_peek_byte(f, 1);
+ if (len < strlen(vmsd->name) + 1) {
+ /* subsection name has be be "section_name/a" */
+ return 0;
+ }
+ size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
+ if (size != len) {
+ return 0;
+ }
+ idstr[size] = 0;
+
+ if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
+ /* it don't have a valid subsection name */
+ return 0;
+ }
+ sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
+ if (sub_vmsd == NULL) {
+ return -ENOENT;
+ }
+ qemu_file_skip(f, 1); /* subsection */
+ qemu_file_skip(f, 1); /* len */
+ qemu_file_skip(f, len); /* idstr */
+ version_id = qemu_get_be32(f);
+
+ ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ const VMStateSubsection *sub = vmsd->subsections;
+
+ while (sub && sub->needed) {
+ if (sub->needed(opaque)) {
+ const VMStateDescription *vmsd = sub->vmsd;
+ uint8_t len;
+
+ qemu_put_byte(f, QEMU_VM_SUBSECTION);
+ len = strlen(vmsd->name);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
+ qemu_put_be32(f, vmsd->version_id);
+ vmstate_save_state(f, vmsd, opaque);
+ }
+ sub++;
+ }
+}
+
+/* bool */
+
+static int get_bool(QEMUFile *f, void *pv, size_t size)
+{
+ bool *v = pv;
+ *v = qemu_get_byte(f);
+ return 0;
+}
+
+static void put_bool(QEMUFile *f, void *pv, size_t size)
+{
+ bool *v = pv;
+ qemu_put_byte(f, *v);
+}
+
+const VMStateInfo vmstate_info_bool = {
+ .name = "bool",
+ .get = get_bool,
+ .put = put_bool,
+};
+
+/* 8 bit int */
+
+static int get_int8(QEMUFile *f, void *pv, size_t size)
+{
+ int8_t *v = pv;
+ qemu_get_s8s(f, v);
+ return 0;
+}
+
+static void put_int8(QEMUFile *f, void *pv, size_t size)
+{
+ int8_t *v = pv;
+ qemu_put_s8s(f, v);
+}
+
+const VMStateInfo vmstate_info_int8 = {
+ .name = "int8",
+ .get = get_int8,
+ .put = put_int8,
+};
+
+/* 16 bit int */
+
+static int get_int16(QEMUFile *f, void *pv, size_t size)
+{
+ int16_t *v = pv;
+ qemu_get_sbe16s(f, v);
+ return 0;
+}
+
+static void put_int16(QEMUFile *f, void *pv, size_t size)
+{
+ int16_t *v = pv;
+ qemu_put_sbe16s(f, v);
+}
+
+const VMStateInfo vmstate_info_int16 = {
+ .name = "int16",
+ .get = get_int16,
+ .put = put_int16,
+};
+
+/* 32 bit int */
+
+static int get_int32(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ qemu_get_sbe32s(f, v);
+ return 0;
+}
+
+static void put_int32(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ qemu_put_sbe32s(f, v);
+}
+
+const VMStateInfo vmstate_info_int32 = {
+ .name = "int32",
+ .get = get_int32,
+ .put = put_int32,
+};
+
+/* 32 bit int. See that the received value is the same than the one
+ in the field */
+
+static int get_int32_equal(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ int32_t v2;
+ qemu_get_sbe32s(f, &v2);
+
+ if (*v == v2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_int32_equal = {
+ .name = "int32 equal",
+ .get = get_int32_equal,
+ .put = put_int32,
+};
+
+/* 32 bit int. See that the received value is the less or the same
+ than the one in the field */
+
+static int get_int32_le(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *old = pv;
+ int32_t new;
+ qemu_get_sbe32s(f, &new);
+
+ if (*old <= new) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_int32_le = {
+ .name = "int32 equal",
+ .get = get_int32_le,
+ .put = put_int32,
+};
+
+/* 64 bit int */
+
+static int get_int64(QEMUFile *f, void *pv, size_t size)
+{
+ int64_t *v = pv;
+ qemu_get_sbe64s(f, v);
+ return 0;
+}
+
+static void put_int64(QEMUFile *f, void *pv, size_t size)
+{
+ int64_t *v = pv;
+ qemu_put_sbe64s(f, v);
+}
+
+const VMStateInfo vmstate_info_int64 = {
+ .name = "int64",
+ .get = get_int64,
+ .put = put_int64,
+};
+
+/* 8 bit unsigned int */
+
+static int get_uint8(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_get_8s(f, v);
+ return 0;
+}
+
+static void put_uint8(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_put_8s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint8 = {
+ .name = "uint8",
+ .get = get_uint8,
+ .put = put_uint8,
+};
+
+/* 16 bit unsigned int */
+
+static int get_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ qemu_get_be16s(f, v);
+ return 0;
+}
+
+static void put_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ qemu_put_be16s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint16 = {
+ .name = "uint16",
+ .get = get_uint16,
+ .put = put_uint16,
+};
+
+/* 32 bit unsigned int */
+
+static int get_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ qemu_get_be32s(f, v);
+ return 0;
+}
+
+static void put_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ qemu_put_be32s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint32 = {
+ .name = "uint32",
+ .get = get_uint32,
+ .put = put_uint32,
+};
+
+/* 32 bit uint. See that the received value is the same than the one
+ in the field */
+
+static int get_uint32_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ uint32_t v2;
+ qemu_get_be32s(f, &v2);
+
+ if (*v == v2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint32_equal = {
+ .name = "uint32 equal",
+ .get = get_uint32_equal,
+ .put = put_uint32,
+};
+
+/* 64 bit unsigned int */
+
+static int get_uint64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_get_be64s(f, v);
+ return 0;
+}
+
+static void put_uint64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_put_be64s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint64 = {
+ .name = "uint64",
+ .get = get_uint64,
+ .put = put_uint64,
+};
+
+/* 64 bit unsigned int. See that the received value is the same than the one
+ in the field */
+
+static int get_uint64_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ uint64_t v2;
+ qemu_get_be64s(f, &v2);
+
+ if (*v == v2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint64_equal = {
+ .name = "int64 equal",
+ .get = get_uint64_equal,
+ .put = put_uint64,
+};
+
+/* 8 bit int. See that the received value is the same than the one
+ in the field */
+
+static int get_uint8_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ uint8_t v2;
+ qemu_get_8s(f, &v2);
+
+ if (*v == v2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint8_equal = {
+ .name = "uint8 equal",
+ .get = get_uint8_equal,
+ .put = put_uint8,
+};
+
+/* 16 bit unsigned int int. See that the received value is the same than the one
+ in the field */
+
+static int get_uint16_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ uint16_t v2;
+ qemu_get_be16s(f, &v2);
+
+ if (*v == v2) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint16_equal = {
+ .name = "uint16 equal",
+ .get = get_uint16_equal,
+ .put = put_uint16,
+};
+
+/* floating point */
+
+static int get_float64(QEMUFile *f, void *pv, size_t size)
+{
+ float64 *v = pv;
+
+ *v = make_float64(qemu_get_be64(f));
+ return 0;
+}
+
+static void put_float64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+
+ qemu_put_be64(f, float64_val(*v));
+}
+
+const VMStateInfo vmstate_info_float64 = {
+ .name = "float64",
+ .get = get_float64,
+ .put = put_float64,
+};
+
+/* uint8_t buffers */
+
+static int get_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_get_buffer(f, v, size);
+ return 0;
+}
+
+static void put_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_put_buffer(f, v, size);
+}
+
+const VMStateInfo vmstate_info_buffer = {
+ .name = "buffer",
+ .get = get_buffer,
+ .put = put_buffer,
+};
+
+/* unused buffers: space that was used for some fields that are
+ not useful anymore */
+
+static int get_unused_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t buf[1024];
+ int block_len;
+
+ while (size > 0) {
+ block_len = MIN(sizeof(buf), size);
+ size -= block_len;
+ qemu_get_buffer(f, buf, block_len);
+ }
+ return 0;
+}
+
+static void put_unused_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ static const uint8_t buf[1024];
+ int block_len;
+
+ while (size > 0) {
+ block_len = MIN(sizeof(buf), size);
+ size -= block_len;
+ qemu_put_buffer(f, buf, block_len);
+ }
+}
+
+const VMStateInfo vmstate_info_unused_buffer = {
+ .name = "unused_buffer",
+ .get = get_unused_buffer,
+ .put = put_unused_buffer,
+};
+
+/* bitmaps (as defined by bitmap.h). Note that size here is the size
+ * of the bitmap in bits. The on-the-wire format of a bitmap is 64
+ * bit words with the bits in big endian order. The in-memory format
+ * is an array of 'unsigned long', which may be either 32 or 64 bits.
+ */
+/* This is the number of 64 bit words sent over the wire */
+#define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64)
+static int get_bitmap(QEMUFile *f, void *pv, size_t size)
+{
+ unsigned long *bmp = pv;
+ int i, idx = 0;
+ for (i = 0; i < BITS_TO_U64S(size); i++) {
+ uint64_t w = qemu_get_be64(f);
+ bmp[idx++] = w;
+ if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
+ bmp[idx++] = w >> 32;
+ }
+ }
+ return 0;
+}
+
+static void put_bitmap(QEMUFile *f, void *pv, size_t size)
+{
+ unsigned long *bmp = pv;
+ int i, idx = 0;
+ for (i = 0; i < BITS_TO_U64S(size); i++) {
+ uint64_t w = bmp[idx++];
+ if (sizeof(unsigned long) == 4 && idx < BITS_TO_LONGS(size)) {
+ w |= ((uint64_t)bmp[idx++]) << 32;
+ }
+ qemu_put_be64(f, w);
+ }
+}
+
+const VMStateInfo vmstate_info_bitmap = {
+ .name = "bitmap",
+ .get = get_bitmap,
+ .put = put_bitmap,
+};