aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/buildtest-template.yml6
-rw-r--r--.gitlab-ci.d/buildtest.yml15
-rw-r--r--.gitlab-ci.d/cirrus.yml39
-rw-r--r--.gitlab-ci.d/cirrus/kvm-build.yml31
-rw-r--r--.gitlab-ci.d/custom-runners.yml2
-rw-r--r--.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml (renamed from .gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml)42
-rw-r--r--.gitlab-ci.d/windows.yml9
-rw-r--r--Kconfig.host3
-rw-r--r--MAINTAINERS28
-rw-r--r--accel/kvm/kvm-all.c62
-rw-r--r--accel/tcg/cpu-exec.c19
-rw-r--r--accel/tcg/cputlb.c1
-rw-r--r--accel/tcg/internal-common.h20
-rw-r--r--accel/tcg/plugin-gen.c1092
-rw-r--r--accel/tcg/plugin-helpers.h5
-rw-r--r--accel/tcg/tb-jmp-cache.h4
-rw-r--r--accel/tcg/tb-maint.c1
-rw-r--r--accel/tcg/tcg-accel-ops.c2
-rw-r--r--accel/tcg/translate-all.c9
-rw-r--r--accel/tcg/translator.c307
-rw-r--r--accel/tcg/user-exec.c11
-rw-r--r--accel/tcg/vcpu-state.h18
-rw-r--r--backends/cryptodev-builtin.c9
-rw-r--r--backends/iommufd.c29
-rw-r--r--backends/trace-events4
-rw-r--r--block/gluster.c69
-rw-r--r--block/nbd.c76
-rw-r--r--block/nfs.c110
-rw-r--r--block/qcow2-bitmap.c2
-rw-r--r--block/ssh.c75
-rw-r--r--bsd-user/bsd-mem.h1
-rw-r--r--bsd-user/mmap.c7
-rw-r--r--bsd-user/qemu.h15
-rw-r--r--bsd-user/signal.c1
-rw-r--r--configs/devices/alpha-softmmu/default.mak5
-rw-r--r--configs/devices/arm-softmmu/default.mak5
-rw-r--r--configs/devices/avr-softmmu/default.mak5
-rw-r--r--configs/devices/cris-softmmu/default.mak5
-rw-r--r--configs/devices/hppa-softmmu/default.mak5
-rw-r--r--configs/devices/i386-softmmu/default.mak11
-rw-r--r--configs/devices/loongarch64-softmmu/default.mak6
-rw-r--r--configs/devices/m68k-softmmu/default.mak13
-rw-r--r--configs/devices/microblaze-softmmu/default.mak9
-rw-r--r--configs/devices/mips-softmmu/common.mak5
-rw-r--r--configs/devices/mips64-softmmu/default.mak4
-rw-r--r--configs/devices/mips64el-softmmu/default.mak10
-rw-r--r--configs/devices/or1k-softmmu/default.mak9
-rw-r--r--configs/devices/ppc-softmmu/default.mak30
-rw-r--r--configs/devices/ppc64-softmmu/default.mak8
-rw-r--r--configs/devices/riscv32-softmmu/default.mak17
-rw-r--r--configs/devices/riscv64-softmmu/default.mak19
-rw-r--r--configs/devices/rx-softmmu/default.mak3
-rw-r--r--configs/devices/s390x-softmmu/default.mak5
-rw-r--r--configs/devices/sh4-softmmu/default.mak7
-rw-r--r--configs/devices/sparc-softmmu/default.mak7
-rw-r--r--configs/devices/sparc64-softmmu/default.mak7
-rw-r--r--configs/devices/tricore-softmmu/default.mak7
-rw-r--r--configs/devices/xtensa-softmmu/default.mak11
-rw-r--r--configs/targets/aarch64-softmmu.mak1
-rw-r--r--configs/targets/arm-softmmu.mak1
-rw-r--r--configs/targets/i386-softmmu.mak1
-rw-r--r--configs/targets/loongarch64-softmmu.mak1
-rw-r--r--configs/targets/microblaze-softmmu.mak1
-rw-r--r--configs/targets/microblazeel-softmmu.mak1
-rw-r--r--configs/targets/mips64el-softmmu.mak1
-rw-r--r--configs/targets/or1k-softmmu.mak1
-rw-r--r--configs/targets/ppc-softmmu.mak1
-rw-r--r--configs/targets/ppc64-softmmu.mak1
-rw-r--r--configs/targets/riscv32-softmmu.mak1
-rw-r--r--configs/targets/riscv64-softmmu.mak1
-rw-r--r--configs/targets/rx-softmmu.mak1
-rw-r--r--configs/targets/x86_64-softmmu.mak1
-rwxr-xr-xconfigure13
-rw-r--r--contrib/plugins/execlog.c5
-rw-r--r--contrib/plugins/howvec.c4
-rw-r--r--cpu-target.c1
-rw-r--r--disas/disas-common.c104
-rw-r--r--disas/disas-host.c129
-rw-r--r--disas/disas-internal.h4
-rw-r--r--disas/disas-mon.c15
-rw-r--r--disas/disas-target.c99
-rw-r--r--disas/disas.c338
-rw-r--r--disas/meson.build8
-rw-r--r--disas/objdump.c37
-rw-r--r--docs/about/deprecated.rst80
-rw-r--r--docs/about/removed-features.rst103
-rw-r--r--docs/devel/kconfig.rst14
-rw-r--r--docs/devel/migration/main.rst2
-rw-r--r--docs/sphinx/qapidoc.py21
-rw-r--r--docs/system/arm/raspi.rst1
-rw-r--r--docs/system/target-sparc.rst12
-rw-r--r--gdbstub/gdbstub.c3
-rw-r--r--gdbstub/user-target.c4
-rw-r--r--hmp-commands.hx23
-rw-r--r--hw/9pfs/xen-9p-backend.c8
-rw-r--r--hw/alpha/Kconfig2
-rw-r--r--hw/arm/Kconfig7
-rw-r--r--hw/arm/boot.c8
-rw-r--r--hw/arm/meson.build2
-rw-r--r--hw/arm/npcm7xx.c3
-rw-r--r--hw/audio/virtio-snd.c2
-rw-r--r--hw/avr/Kconfig3
-rw-r--r--hw/block/pflash_cfi01.c8
-rw-r--r--hw/char/omap_uart.c49
-rw-r--r--hw/char/stm32l4x5_usart.c2
-rw-r--r--hw/core/Kconfig9
-rw-r--r--hw/core/cpu-common.c4
-rw-r--r--hw/core/machine-smp.c84
-rw-r--r--hw/core/machine.c9
-rw-r--r--hw/core/meson.build2
-rw-r--r--hw/cris/Kconfig2
-rw-r--r--hw/display/meson.build14
-rw-r--r--hw/display/vga_int.h1
-rw-r--r--hw/display/vhost-user-gpu.c32
-rw-r--r--hw/display/virtio-gpu-udmabuf.c27
-rw-r--r--hw/display/virtio-gpu.c30
-rw-r--r--hw/display/xenfb.c8
-rw-r--r--hw/dma/xlnx_dpdma.c68
-rw-r--r--hw/gpio/stm32l4x5_gpio.c6
-rw-r--r--hw/gpio/zaurus.c61
-rw-r--r--hw/hppa/Kconfig2
-rw-r--r--hw/hppa/machine.c16
-rw-r--r--hw/hyperv/hyperv.c25
-rw-r--r--hw/i386/Kconfig13
-rw-r--r--hw/i386/fw_cfg.c2
-rw-r--r--hw/i386/meson.build7
-rw-r--r--hw/i386/microvm.c2
-rw-r--r--hw/i386/monitor.c46
-rw-r--r--hw/i386/pc.c18
-rw-r--r--hw/i386/pc_piix.c6
-rw-r--r--hw/i386/pc_sysfw.c11
-rw-r--r--hw/i386/x86-common.c1007
-rw-r--r--hw/i386/x86-cpu.c97
-rw-r--r--hw/i386/x86.c1057
-rw-r--r--hw/ide/core.c21
-rw-r--r--hw/input/tsc2005.c135
-rw-r--r--hw/intc/Kconfig2
-rw-r--r--hw/intc/arm_gic.c4
-rw-r--r--hw/intc/ioapic-stub.c29
-rw-r--r--hw/intc/loongarch_ipi.c19
-rw-r--r--hw/intc/loongson_ipi.c368
-rw-r--r--hw/intc/meson.build4
-rw-r--r--hw/intc/s390_flic.c6
-rw-r--r--hw/intc/s390_flic_kvm.c28
-rw-r--r--hw/intc/trace-events8
-rw-r--r--hw/loongarch/Kconfig5
-rw-r--r--hw/loongarch/acpi-build.c89
-rw-r--r--hw/loongarch/boot.c339
-rw-r--r--hw/loongarch/fw_cfg.c2
-rw-r--r--hw/loongarch/fw_cfg.h2
-rw-r--r--hw/loongarch/meson.build3
-rw-r--r--hw/loongarch/virt.c669
-rw-r--r--hw/m68k/Kconfig10
-rw-r--r--hw/microblaze/Kconfig6
-rw-r--r--hw/mips/Kconfig13
-rw-r--r--hw/mips/loongson3_bootp.c1
-rw-r--r--hw/mips/loongson3_virt.c20
-rw-r--r--hw/mips/meson.build2
-rw-r--r--hw/misc/edu.c35
-rw-r--r--hw/openrisc/Kconfig6
-rw-r--r--hw/openrisc/meson.build4
-rw-r--r--hw/ppc/Kconfig27
-rw-r--r--hw/ppc/meson.build4
-rw-r--r--hw/ppc/ppc405_boards.c1
-rw-r--r--hw/ppc/ppc440_bamboo.c1
-rw-r--r--hw/ppc/sam460ex.c1
-rw-r--r--hw/ppc/spapr_pci.c7
-rw-r--r--hw/ppc/virtex_ml507.c1
-rw-r--r--hw/remote/vfio-user-obj.c4
-rw-r--r--hw/riscv/Kconfig18
-rw-r--r--hw/riscv/meson.build2
-rw-r--r--hw/rtc/ls7a_rtc.c2
-rw-r--r--hw/rtc/mc146818rtc.c12
-rw-r--r--hw/rx/Kconfig3
-rw-r--r--hw/s390x/Kconfig2
-rw-r--r--hw/s390x/css.c10
-rw-r--r--hw/s390x/event-facility.c13
-rw-r--r--hw/s390x/s390-virtio-ccw.c50
-rw-r--r--hw/s390x/sclp.c15
-rw-r--r--hw/sh4/Kconfig4
-rw-r--r--hw/sh4/meson.build2
-rw-r--r--hw/sparc/Kconfig4
-rw-r--r--hw/sparc64/Kconfig4
-rw-r--r--hw/sparc64/sun4u.c7
-rw-r--r--hw/tricore/Kconfig4
-rw-r--r--hw/ufs/ufs.c8
-rw-r--r--hw/usb/dev-network.c8
-rw-r--r--hw/usb/xen-usb.c14
-rw-r--r--hw/vfio/ap.c35
-rw-r--r--hw/vfio/ccw.c56
-rw-r--r--hw/vfio/common.c119
-rw-r--r--hw/vfio/container-base.c18
-rw-r--r--hw/vfio/container.c101
-rw-r--r--hw/vfio/cpr.c4
-rw-r--r--hw/vfio/display.c54
-rw-r--r--hw/vfio/helpers.c36
-rw-r--r--hw/vfio/igd.c35
-rw-r--r--hw/vfio/iommufd.c109
-rw-r--r--hw/vfio/migration.c180
-rw-r--r--hw/vfio/pci-quirks.c50
-rw-r--r--hw/vfio/pci.c280
-rw-r--r--hw/vfio/pci.h13
-rw-r--r--hw/vfio/platform.c66
-rw-r--r--hw/vfio/spapr.c28
-rw-r--r--hw/vfio/trace-events3
-rw-r--r--hw/virtio/vhost-vdpa.c5
-rw-r--r--hw/xen/xen-bus.c4
-rw-r--r--hw/xen/xen-hvm-common.c2
-rw-r--r--hw/xen/xen-legacy-backend.c16
-rw-r--r--hw/xen/xen-mapcache.c208
-rw-r--r--hw/xenpv/xen_machine_pv.c5
-rw-r--r--hw/xtensa/Kconfig7
-rw-r--r--hw/xtensa/xtfpga.c9
-rw-r--r--include/disas/disas.h9
-rw-r--r--include/exec/cpu-all.h38
-rw-r--r--include/exec/cpu-common.h49
-rw-r--r--include/exec/cpu_ldst.h10
-rw-r--r--include/exec/exec-all.h3
-rw-r--r--include/exec/helper-gen-common.h4
-rw-r--r--include/exec/helper-gen.h.inc24
-rw-r--r--include/exec/helper-proto-common.h4
-rw-r--r--include/exec/memory.h60
-rw-r--r--include/exec/page-protection.h41
-rw-r--r--include/exec/plugin-gen.h11
-rw-r--r--include/exec/translation-block.h1
-rw-r--r--include/exec/translator.h74
-rw-r--r--include/glib-compat.h27
-rw-r--r--include/hw/core/cpu.h49
-rw-r--r--include/hw/i386/x86.h27
-rw-r--r--include/hw/intc/i8259.h2
-rw-r--r--include/hw/intc/loongarch_extioi.h1
-rw-r--r--include/hw/intc/loongson_ipi.h (renamed from include/hw/intc/loongarch_ipi.h)12
-rw-r--r--include/hw/loongarch/boot.h119
-rw-r--r--include/hw/loongarch/virt.h17
-rw-r--r--include/hw/nvram/fw_cfg.h2
-rw-r--r--include/hw/pci-host/ls7a.h2
-rw-r--r--include/hw/pci/pcie.h3
-rw-r--r--include/hw/pci/pcie_aer.h38
-rw-r--r--include/hw/pci/pcie_sriov.h8
-rw-r--r--include/hw/qdev-core.h1
-rw-r--r--include/hw/rtc/mc146818rtc.h2
-rw-r--r--include/hw/s390x/adapter.h4
-rw-r--r--include/hw/s390x/css.h6
-rw-r--r--include/hw/s390x/event-facility.h2
-rw-r--r--include/hw/s390x/s390-virtio-ccw.h10
-rw-r--r--include/hw/s390x/s390_flic.h1
-rw-r--r--include/hw/s390x/sclp.h2
-rw-r--r--include/hw/vfio/vfio-common.h45
-rw-r--r--include/hw/vfio/vfio-container-base.h55
-rw-r--r--include/hw/virtio/virtio-gpu.h5
-rw-r--r--include/hw/xen/xen-legacy-backend.h14
-rw-r--r--include/hw/xen/xen_pvdev.h1
-rw-r--r--include/migration/colo.h2
-rw-r--r--include/migration/misc.h8
-rw-r--r--include/monitor/hmp-target.h11
-rw-r--r--include/monitor/hmp.h1
-rw-r--r--include/net/announce.h4
-rw-r--r--include/qemu/bitmap.h19
-rw-r--r--include/qemu/coroutine.h4
-rw-r--r--include/qemu/cutils.h32
-rw-r--r--include/qemu/lockable.h4
-rw-r--r--include/qemu/log.h1
-rw-r--r--include/qemu/option.h2
-rw-r--r--include/qemu/plugin.h123
-rw-r--r--include/qemu/qemu-plugin.h95
-rw-r--r--include/qemu/typedefs.h26
-rw-r--r--include/qemu/uri.h99
-rw-r--r--include/semihosting/uaccess.h1
-rw-r--r--include/sysemu/device_tree.h1
-rw-r--r--include/sysemu/iommufd.h6
-rw-r--r--include/sysemu/kvm.h5
-rw-r--r--include/sysemu/numa.h8
-rw-r--r--include/sysemu/xen-mapcache.h11
-rw-r--r--include/tcg/helper-info.h3
-rw-r--r--include/tcg/tcg-op-common.h4
-rw-r--r--include/tcg/tcg-op-gvec-common.h2
-rw-r--r--include/tcg/tcg-opc.h4
-rw-r--r--include/tcg/tcg.h27
-rw-r--r--include/ui/console.h20
-rw-r--r--include/ui/dmabuf.h49
-rw-r--r--include/user/abitypes.h (renamed from include/exec/user/abitypes.h)4
-rw-r--r--include/user/syscall-trace.h2
-rw-r--r--include/user/thunk.h (renamed from include/exec/user/thunk.h)10
-rw-r--r--linux-user/arm/cpu_loop.c1
-rw-r--r--linux-user/elfload.c55
-rw-r--r--linux-user/hppa/cpu_loop.c14
-rw-r--r--linux-user/hppa/signal.c6
-rw-r--r--linux-user/hppa/target_cpu.h4
-rw-r--r--linux-user/mmap.c11
-rw-r--r--linux-user/qemu.h12
-rw-r--r--linux-user/signal.c1
-rw-r--r--linux-user/syscall.c9
-rw-r--r--linux-user/thunk.c2
-rw-r--r--linux-user/user-internals.h2
-rw-r--r--meson.build130
-rw-r--r--meson_options.txt2
-rw-r--r--migration/block.c1019
-rw-r--r--migration/block.h52
-rw-r--r--migration/colo-stubs.c3
-rw-r--r--migration/colo.c13
-rw-r--r--migration/fd.c12
-rw-r--r--migration/meson.build4
-rw-r--r--migration/migration-hmp-cmds.c99
-rw-r--r--migration/migration.c137
-rw-r--r--migration/migration.h16
-rw-r--r--migration/options.c229
-rw-r--r--migration/options.h13
-rw-r--r--migration/postcopy-ram.c4
-rw-r--r--migration/qemu-file.c78
-rw-r--r--migration/qemu-file.h4
-rw-r--r--migration/ram-compress.c564
-rw-r--r--migration/ram-compress.h77
-rw-r--r--migration/ram.c181
-rw-r--r--migration/savevm.c5
-rw-r--r--migration/trace-events4
-rw-r--r--migration/vmstate.c7
-rw-r--r--monitor/hmp-cmds.c17
-rw-r--r--plugins/api.c120
-rw-r--r--plugins/core.c158
-rw-r--r--plugins/plugin.h12
-rw-r--r--plugins/qemu-plugins.symbols2
-rw-r--r--qapi/machine-target.json7
-rw-r--r--qapi/meson.build1
-rw-r--r--qapi/migration.json212
-rw-r--r--qapi/qapi-schema.json1
-rw-r--r--qapi/vfio.json67
-rw-r--r--qemu-options.hx19
-rw-r--r--qga/commands-common-ssh.c50
-rw-r--r--qga/commands-common-ssh.h10
-rw-r--r--qga/commands-posix-ssh.c59
-rw-r--r--qga/commands-posix.c404
-rw-r--r--qga/commands-win32.c1
-rw-r--r--qga/commands-windows-ssh.c712
-rw-r--r--qga/commands-windows-ssh.h26
-rw-r--r--qga/meson.build13
-rw-r--r--qga/qapi-schema.json24
-rw-r--r--qom/object.c8
-rwxr-xr-xscripts/checkpatch.pl11
-rw-r--r--scripts/ci/setup/build-environment.yml16
-rw-r--r--scripts/coverity-scan/COMPONENTS.md2
-rw-r--r--scripts/meson-buildoptions.sh4
-rw-r--r--scripts/qapi/commands.py2
-rw-r--r--scripts/qapi/events.py2
-rw-r--r--scripts/qapi/gen.py2
-rw-r--r--scripts/qapi/introspect.py15
-rw-r--r--scripts/qapi/schema.py225
-rw-r--r--scripts/qapi/types.py12
-rw-r--r--scripts/qapi/visit.py24
-rwxr-xr-xscripts/update-linux-headers.sh80
-rw-r--r--stubs/meson.build6
-rw-r--r--stubs/target-monitor-defs.c3
-rw-r--r--system/device_tree-stub.c10
-rw-r--r--system/device_tree.c14
-rw-r--r--system/dma-helpers.c4
-rw-r--r--system/memory.c17
-rw-r--r--system/meson.build4
-rw-r--r--system/physmem.c185
-rw-r--r--system/vl.c18
-rw-r--r--target/Kconfig3
-rw-r--r--target/alpha/cpu.c32
-rw-r--r--target/alpha/helper.c9
-rw-r--r--target/alpha/translate.c126
-rw-r--r--target/arm/Kconfig4
-rw-r--r--target/arm/cpu.c2
-rw-r--r--target/arm/cpu.h1
-rw-r--r--target/arm/helper.h68
-rw-r--r--target/arm/hvf/hvf.c160
-rw-r--r--target/arm/ptw.c1
-rw-r--r--target/arm/tcg/a64.decode317
-rw-r--r--target/arm/tcg/gengvec.c1672
-rw-r--r--target/arm/tcg/gengvec64.c190
-rw-r--r--target/arm/tcg/helper-a64.h12
-rw-r--r--target/arm/tcg/m_helper.c1
-rw-r--r--target/arm/tcg/meson.build2
-rw-r--r--target/arm/tcg/mte_helper.c1
-rw-r--r--target/arm/tcg/neon_helper.c5
-rw-r--r--target/arm/tcg/sve_helper.c1
-rw-r--r--target/arm/tcg/translate-a64.c3115
-rw-r--r--target/arm/tcg/translate-a64.h4
-rw-r--r--target/arm/tcg/translate-neon.c136
-rw-r--r--target/arm/tcg/translate-sve.c145
-rw-r--r--target/arm/tcg/translate-vfp.c54
-rw-r--r--target/arm/tcg/translate.c1600
-rw-r--r--target/arm/tcg/translate.h51
-rw-r--r--target/arm/tcg/vec_helper.c227
-rw-r--r--target/arm/vfp_helper.c30
-rw-r--r--target/avr/cpu.c2
-rw-r--r--target/avr/helper.c1
-rw-r--r--target/avr/translate.c11
-rw-r--r--target/cris/mmu.c5
-rw-r--r--target/cris/translate.c37
-rw-r--r--target/cris/translate_v10.c.inc30
-rw-r--r--target/hexagon/README11
-rw-r--r--target/hexagon/attribs_def.h.inc3
-rw-r--r--target/hexagon/cpu.c2
-rw-r--r--target/hexagon/decode.c50
-rwxr-xr-xtarget/hexagon/gen_analyze_funcs.py74
-rwxr-xr-xtarget/hexagon/gen_helper_funcs.py21
-rwxr-xr-xtarget/hexagon/gen_helper_protos.py31
-rw-r--r--target/hexagon/gen_idef_parser_funcs.py5
-rwxr-xr-xtarget/hexagon/gen_op_attribs.py5
-rwxr-xr-xtarget/hexagon/gen_op_regs.py125
-rwxr-xr-xtarget/hexagon/gen_opcodes_def.py4
-rwxr-xr-xtarget/hexagon/gen_printinsn.py5
-rwxr-xr-xtarget/hexagon/gen_shortcode.py63
-rw-r--r--target/hexagon/gen_tcg.h5
-rwxr-xr-xtarget/hexagon/gen_tcg_func_table.py5
-rwxr-xr-xtarget/hexagon/gen_tcg_funcs.py21
-rwxr-xr-xtarget/hexagon/gen_trans_funcs.py26
-rwxr-xr-xtarget/hexagon/hex_common.py189
-rw-r--r--target/hexagon/insn.h5
-rw-r--r--target/hexagon/macros.h6
-rw-r--r--target/hexagon/meson.build55
-rw-r--r--target/hexagon/mmvec/decode_ext_mmvec.c30
-rw-r--r--target/hexagon/opcodes.c35
-rw-r--r--target/hexagon/opcodes.h4
-rw-r--r--target/hexagon/translate.c88
-rw-r--r--target/hexagon/translate.h119
-rw-r--r--target/hppa/cpu.c86
-rw-r--r--target/hppa/cpu.h80
-rw-r--r--target/hppa/fpu_helper.c26
-rw-r--r--target/hppa/gdbstub.c6
-rw-r--r--target/hppa/helper.c66
-rw-r--r--target/hppa/helper.h3
-rw-r--r--target/hppa/int_helper.c33
-rw-r--r--target/hppa/mem_helper.c96
-rw-r--r--target/hppa/op_helper.c17
-rw-r--r--target/hppa/sys_helper.c12
-rw-r--r--target/hppa/translate.c1168
-rw-r--r--target/i386/Kconfig4
-rw-r--r--target/i386/cpu.c39
-rw-r--r--target/i386/cpu.h8
-rw-r--r--target/i386/gdbstub.c2
-rw-r--r--target/i386/helper.c2
-rw-r--r--target/i386/helper.h11
-rw-r--r--target/i386/nvmm/nvmm-all.c2
-rw-r--r--target/i386/tcg/decode-new.c.inc628
-rw-r--r--target/i386/tcg/decode-new.h24
-rw-r--r--target/i386/tcg/emit.c.inc1600
-rw-r--r--target/i386/tcg/int_helper.c34
-rw-r--r--target/i386/tcg/shift_helper_template.h.inc108
-rw-r--r--target/i386/tcg/sysemu/excp_helper.c1
-rw-r--r--target/i386/tcg/translate.c3785
-rw-r--r--target/i386/whpx/whpx-all.c2
-rw-r--r--target/loongarch/cpu.c11
-rw-r--r--target/loongarch/cpu.h18
-rw-r--r--target/loongarch/cpu_helper.c9
-rw-r--r--target/loongarch/kvm/kvm.c16
-rw-r--r--target/loongarch/machine.c30
-rw-r--r--target/loongarch/tcg/tlb_helper.c1
-rw-r--r--target/loongarch/tcg/translate.c8
-rw-r--r--target/m68k/helper.c1
-rw-r--r--target/m68k/translate.c9
-rw-r--r--target/microblaze/Kconfig1
-rw-r--r--target/microblaze/cpu.c2
-rw-r--r--target/microblaze/helper.c3
-rw-r--r--target/microblaze/mmu.c1
-rw-r--r--target/microblaze/translate.c11
-rw-r--r--target/mips/sysemu/physaddr.c1
-rw-r--r--target/mips/tcg/exception.c2
-rw-r--r--target/mips/tcg/sysemu/special_helper.c2
-rw-r--r--target/mips/tcg/sysemu/tlb_helper.c1
-rw-r--r--target/mips/tcg/translate.c9
-rw-r--r--target/openrisc/Kconfig1
-rw-r--r--target/openrisc/cpu.c2
-rw-r--r--target/openrisc/mmu.c1
-rw-r--r--target/openrisc/translate.c11
-rw-r--r--target/ppc/Kconfig1
-rw-r--r--target/ppc/cpu_init.c9
-rw-r--r--target/ppc/internal.h1
-rw-r--r--target/ppc/kvm.c17
-rw-r--r--target/ppc/mmu-hash32.c1
-rw-r--r--target/ppc/mmu-hash64.c3
-rw-r--r--target/ppc/mmu-radix64.c1
-rw-r--r--target/ppc/mmu-radix64.h2
-rw-r--r--target/ppc/mmu_common.c1
-rw-r--r--target/ppc/mmu_helper.c1
-rw-r--r--target/ppc/translate.c9
-rw-r--r--target/riscv/Kconfig2
-rw-r--r--target/riscv/cpu_helper.c1
-rw-r--r--target/riscv/kvm/kvm-cpu.c4
-rw-r--r--target/riscv/pmp.c1
-rw-r--r--target/riscv/tcg/tcg-cpu.c4
-rw-r--r--target/riscv/translate.c24
-rw-r--r--target/riscv/vector_helper.c1
-rw-r--r--target/rx/cpu.c3
-rw-r--r--target/rx/translate.c35
-rw-r--r--target/s390x/Kconfig2
-rw-r--r--target/s390x/cpu_features.c17
-rw-r--r--target/s390x/cpu_features.h1
-rw-r--r--target/s390x/cpu_models.c34
-rw-r--r--target/s390x/cpu_models.h4
-rw-r--r--target/s390x/cpu_models_sysemu.c13
-rw-r--r--target/s390x/kvm/kvm.c28
-rw-r--r--target/s390x/mmu_helper.c1
-rw-r--r--target/s390x/sigp.c17
-rw-r--r--target/s390x/tcg/mem_helper.c1
-rw-r--r--target/s390x/tcg/translate.c26
-rw-r--r--target/sh4/Kconfig2
-rw-r--r--target/sh4/cpu.c4
-rw-r--r--target/sh4/helper.c1
-rw-r--r--target/sh4/translate.c42
-rw-r--r--target/sparc/cpu.c68
-rw-r--r--target/sparc/helper.h11
-rw-r--r--target/sparc/insns.decode2
-rw-r--r--target/sparc/ldst_helper.c1
-rw-r--r--target/sparc/mmu_helper.c1
-rw-r--r--target/sparc/translate.c138
-rw-r--r--target/sparc/vis_helper.c189
-rw-r--r--target/tricore/cpu.c2
-rw-r--r--target/tricore/helper.c1
-rw-r--r--target/tricore/translate.c9
-rw-r--r--target/xtensa/mmu_helper.c1
-rw-r--r--target/xtensa/op_helper.c1
-rw-r--r--target/xtensa/translate.c12
-rw-r--r--tcg/i386/tcg-target.c.inc76
-rw-r--r--tcg/loongarch64/tcg-target.c.inc103
-rw-r--r--tcg/optimize.c110
-rw-r--r--tcg/tcg-op-gvec.c30
-rw-r--r--tcg/tcg-op-ldst.c6
-rw-r--r--tcg/tcg-op.c8
-rw-r--r--tcg/tcg.c90
-rw-r--r--tcg/tci.c1
-rw-r--r--tests/bench/bufferiszero-bench.c47
-rw-r--r--tests/bench/meson.build1
-rw-r--r--tests/docker/dockerfiles/alpine.docker4
-rw-r--r--tests/docker/dockerfiles/centos9.docker4
-rw-r--r--tests/docker/dockerfiles/debian-all-test-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian-amd64-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-arm64-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-armel-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-armhf-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-hexagon-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian-i686-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-legacy-test-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian-loongarch-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian-mips64el-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-mipsel-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-ppc64el-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-riscv64-cross.docker3
-rw-r--r--tests/docker/dockerfiles/debian-s390x-cross.docker4
-rw-r--r--tests/docker/dockerfiles/debian-tricore-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian-xtensa-cross.docker1
-rw-r--r--tests/docker/dockerfiles/debian.docker4
-rw-r--r--tests/docker/dockerfiles/fedora-cris-cross.docker1
-rw-r--r--tests/docker/dockerfiles/fedora-win64-cross.docker2
-rw-r--r--tests/docker/dockerfiles/fedora.docker4
-rw-r--r--tests/docker/dockerfiles/opensuse-leap.docker4
-rw-r--r--tests/docker/dockerfiles/ubuntu2204.docker4
-rw-r--r--tests/guest-debug/test_gdbstub.py2
-rw-r--r--tests/lcitool/projects/qemu-minimal.yml1
-rw-r--r--tests/lcitool/projects/qemu-win-installer.yml4
-rw-r--r--tests/lcitool/projects/qemu.yml18
-rwxr-xr-xtests/lcitool/refresh5
-rw-r--r--tests/plugin/inline.c130
-rwxr-xr-xtests/qapi-schema/test-qapi.py9
-rwxr-xr-xtests/qemu-iotests/183147
-rw-r--r--tests/qemu-iotests/183.out66
-rw-r--r--tests/qemu-iotests/common.filter7
-rw-r--r--tests/qtest/arm-cpu-features.c4
-rw-r--r--tests/qtest/boot-serial-test.c10
-rw-r--r--tests/qtest/dm163-test.c194
-rw-r--r--tests/qtest/drive_del-test.c7
-rw-r--r--tests/qtest/ide-test.c47
-rw-r--r--tests/qtest/m48t59-test.c11
-rw-r--r--tests/qtest/meson.build5
-rw-r--r--tests/qtest/migration-test.c186
-rw-r--r--tests/qtest/numa-test.c4
-rw-r--r--tests/qtest/nvme-test.c2
-rw-r--r--tests/qtest/ufs-test.c2
-rw-r--r--tests/tcg/arm/Makefile.softmmu-target2
-rw-r--r--tests/tcg/hexagon/hvx_misc.c16
-rw-r--r--tests/tcg/i386/test-i386.c25
-rw-r--r--tests/tcg/sh4/Makefile.target6
-rw-r--r--tests/tcg/sh4/test-addv.c27
-rw-r--r--tests/tcg/sh4/test-subv.c30
-rw-r--r--tests/unit/test-smp-parse.c16
-rw-r--r--ui/cocoa.m13
-rw-r--r--ui/console.c4
-rw-r--r--ui/dbus-console.c9
-rw-r--r--ui/dbus-listener.c71
-rw-r--r--ui/dmabuf.c229
-rw-r--r--ui/egl-headless.c23
-rw-r--r--ui/egl-helpers.c59
-rw-r--r--ui/gtk-egl.c53
-rw-r--r--ui/gtk-gl-area.c42
-rw-r--r--ui/gtk.c32
-rw-r--r--ui/meson.build1
-rw-r--r--ui/sdl2.c1
-rw-r--r--ui/spice-display.c50
-rw-r--r--ui/trace-events1
-rw-r--r--ui/vnc.c5
-rw-r--r--util/bufferiszero.c443
-rw-r--r--util/error-report.c10
-rw-r--r--util/log.c4
-rw-r--r--util/meson.build2
-rw-r--r--util/uri.c1466
597 files changed, 18057 insertions, 20126 deletions
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml
index 22045add80..278a5ea966 100644
--- a/.gitlab-ci.d/buildtest-template.yml
+++ b/.gitlab-ci.d/buildtest-template.yml
@@ -26,10 +26,10 @@
then
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
fi || exit 1;
- - make -j"$JOBS"
+ - $MAKE -j"$JOBS"
- if test -n "$MAKE_CHECK_ARGS";
then
- make -j"$JOBS" $MAKE_CHECK_ARGS ;
+ $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ;
fi
- ccache --show-stats
@@ -60,7 +60,7 @@
- cd build
- find . -type f -exec touch {} +
# Avoid recompiling by hiding ninja with NINJA=":"
- - make NINJA=":" $MAKE_CHECK_ARGS
+ - $MAKE NINJA=":" $MAKE_CHECK_ARGS
.native_test_job_template:
extends: .common_test_job_template
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index 6394b8f41e..91c57efded 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -342,7 +342,7 @@ build-tcg-disabled:
- cd tests/qemu-iotests/
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
- 170 171 183 184 192 194 208 221 226 227 236 253 277 image-fleecing
+ 170 171 184 192 194 208 221 226 227 236 253 277 image-fleecing
- ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122
124 132 139 142 144 145 151 152 155 157 165 194 196 200 202
208 209 216 218 227 234 246 247 248 250 254 255 257 258
@@ -575,6 +575,9 @@ tsan-build:
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
--enable-trace-backends=ust --disable-slirp
TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user
+ # Remove when we switch to a distro with clang >= 18
+ # https://github.com/google/sanitizers/issues/1716
+ MAKE: setarch -R make
# gcov is a GCC features
gcov:
@@ -648,6 +651,9 @@ build-tci:
- make check-tcg
# Check our reduced build configurations
+# requires libfdt: aarch64, arm, loongarch64, microblaze, microblazeel,
+# or1k, ppc64, riscv32, riscv64, rx
+# fails qtest without boards: i386, x86_64
build-without-defaults:
extends: .native_build_job_template
needs:
@@ -661,8 +667,11 @@ build-without-defaults:
--disable-pie
--disable-qom-cast-debug
--disable-strip
- TARGETS: avr-softmmu s390x-softmmu sh4-softmmu
- sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user
+ TARGETS: alpha-softmmu avr-softmmu cris-softmmu hppa-softmmu m68k-softmmu
+ mips-softmmu mips64-softmmu mipsel-softmmu mips64el-softmmu
+ ppc-softmmu s390x-softmmu sh4-softmmu sh4eb-softmmu sparc-softmmu
+ sparc64-softmmu tricore-softmmu xtensa-softmmu xtensaeb-softmmu
+ hexagon-linux-user i386-linux-user s390x-linux-user
MAKE_CHECK_ARGS: check
build-libvhost-user:
diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml
index 4671f069c3..75df1273bc 100644
--- a/.gitlab-ci.d/cirrus.yml
+++ b/.gitlab-ci.d/cirrus.yml
@@ -57,6 +57,7 @@ x64-freebsd-13-build:
CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update; pkg upgrade -y
INSTALL_COMMAND: pkg install -y
+ CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
TEST_TARGETS: check
aarch64-macos-13-base-build:
@@ -72,6 +73,7 @@ aarch64-macos-13-base-build:
INSTALL_COMMAND: brew install
PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin
PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig
+ CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblazeel-softmmu,mips64-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4-softmmu,xtensaeb-softmmu
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
aarch64-macos-14-base-build:
@@ -89,40 +91,3 @@ aarch64-macos-14-base-build:
PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
QEMU_JOB_OPTIONAL: 1
-
-
-# The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job
-.cirrus_kvm_job:
- extends: .base_job_template
- stage: build
- image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
- needs: []
- timeout: 80m
- script:
- - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g"
- -e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g"
- -e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g"
- -e "s|[@]NAME@|$NAME|g"
- -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g"
- -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g"
- <.gitlab-ci.d/cirrus/kvm-build.yml >.gitlab-ci.d/cirrus/$NAME.yml
- - cat .gitlab-ci.d/cirrus/$NAME.yml
- - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml
- variables:
- QEMU_JOB_CIRRUS: 1
- QEMU_JOB_OPTIONAL: 1
-
-
-x86-netbsd:
- extends: .cirrus_kvm_job
- variables:
- NAME: netbsd
- CONFIGURE_ARGS: --target-list=x86_64-softmmu,ppc64-softmmu,aarch64-softmmu
- TEST_TARGETS: check
-
-x86-openbsd:
- extends: .cirrus_kvm_job
- variables:
- NAME: openbsd
- CONFIGURE_ARGS: --target-list=i386-softmmu,riscv64-softmmu,mips64-softmmu
- TEST_TARGETS: check
diff --git a/.gitlab-ci.d/cirrus/kvm-build.yml b/.gitlab-ci.d/cirrus/kvm-build.yml
deleted file mode 100644
index a93881aa8b..0000000000
--- a/.gitlab-ci.d/cirrus/kvm-build.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-container:
- image: fedora:35
- cpu: 4
- memory: 8Gb
- kvm: true
-
-env:
- CIRRUS_CLONE_DEPTH: 1
- CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@"
- CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@"
- CI_COMMIT_SHA: "@CI_COMMIT_SHA@"
-
-@NAME@_task:
- @NAME@_vm_cache:
- folder: $HOME/.cache/qemu-vm
- install_script:
- - dnf update -y
- - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget meson
- clone_script:
- - git clone --depth 100 "$CI_REPOSITORY_URL" .
- - git fetch origin "$CI_COMMIT_REF_NAME"
- - git reset --hard "$CI_COMMIT_SHA"
- build_script:
- - if [ -f $HOME/.cache/qemu-vm/images/@NAME@.img ]; then
- make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN)
- EXTRA_CONFIGURE_OPTS="@CONFIGURE_ARGS@"
- BUILD_TARGET="@TEST_TARGETS@" ;
- else
- make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) BUILD_TARGET=help
- EXTRA_CONFIGURE_OPTS="--disable-system --disable-user --disable-tools" ;
- fi
diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml
index a0e79acd39..29e52df283 100644
--- a/.gitlab-ci.d/custom-runners.yml
+++ b/.gitlab-ci.d/custom-runners.yml
@@ -29,7 +29,7 @@
junit: build/meson-logs/testlog.junit.xml
include:
- - local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml'
+ - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
- local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml'
diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml
index cdae6c5212..25935048e2 100644
--- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml
+++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml
@@ -1,34 +1,32 @@
-# All ubuntu-20.04 jobs should run successfully in an environment
+# All ubuntu-22.04 jobs should run successfully in an environment
# setup by the scripts/ci/setup/build-environment.yml task
-# "Install basic packages to build QEMU on Ubuntu 20.04/20.04"
+# "Install basic packages to build QEMU on Ubuntu 22.04"
-ubuntu-20.04-s390x-all-linux-static:
+ubuntu-22.04-s390x-all-linux:
extends: .custom_runner_template
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
script:
- # --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
- # --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
- mkdir build
- cd build
- - ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
+ - ../configure --enable-debug --disable-system --disable-tools --disable-docs
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync check-tcg
- make --output-sync -j`nproc` check
-ubuntu-20.04-s390x-all:
+ubuntu-22.04-s390x-all-system:
extends: .custom_runner_template
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
timeout: 75m
rules:
@@ -37,17 +35,17 @@ ubuntu-20.04-s390x-all:
script:
- mkdir build
- cd build
- - ../configure --disable-libssh
+ - ../configure --disable-user
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
-ubuntu-20.04-s390x-alldbg:
+ubuntu-22.04-s390x-alldbg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -59,18 +57,18 @@ ubuntu-20.04-s390x-alldbg:
script:
- mkdir build
- cd build
- - ../configure --enable-debug --disable-libssh
+ - ../configure --enable-debug
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make clean
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
-ubuntu-20.04-s390x-clang:
+ubuntu-22.04-s390x-clang:
extends: .custom_runner_template
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -82,16 +80,16 @@ ubuntu-20.04-s390x-clang:
script:
- mkdir build
- cd build
- - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers
+ - ../configure --cc=clang --cxx=clang++ --enable-sanitizers
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
-ubuntu-20.04-s390x-tci:
+ubuntu-22.04-s390x-tci:
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -103,16 +101,16 @@ ubuntu-20.04-s390x-tci:
script:
- mkdir build
- cd build
- - ../configure --disable-libssh --enable-tcg-interpreter
+ - ../configure --enable-tcg-interpreter
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
-ubuntu-20.04-s390x-notcg:
+ubuntu-22.04-s390x-notcg:
extends: .custom_runner_template
needs: []
stage: build
tags:
- - ubuntu_20.04
+ - ubuntu_22.04
- s390x
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -124,7 +122,7 @@ ubuntu-20.04-s390x-notcg:
script:
- mkdir build
- cd build
- - ../configure --disable-libssh --disable-tcg
+ - ../configure --disable-tcg
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
- make --output-sync -j`nproc`
- make --output-sync -j`nproc` check
diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml
index 94834269ec..a83f23a786 100644
--- a/.gitlab-ci.d/windows.yml
+++ b/.gitlab-ci.d/windows.yml
@@ -1,9 +1,7 @@
msys2-64bit:
extends: .base_job_template
tags:
- - shared-windows
- - windows
- - windows-1809
+ - saas-windows-medium-amd64
cache:
key: "$CI_JOB_NAME"
paths:
@@ -24,10 +22,7 @@ msys2-64bit:
# changed to compile QEMU with the --without-default-devices switch
# for this job, because otherwise the build could not complete within
# the project timeout.
- CONFIGURE_ARGS: --target-list=x86_64-softmmu --without-default-devices -Ddebug=false -Doptimization=0
- # qTests don't run successfully with "--without-default-devices",
- # so let's exclude the qtests from CI for now.
- TEST_ARGS: --no-suite qtest
+ CONFIGURE_ARGS: --target-list=sparc-softmmu --without-default-devices -Ddebug=false -Doptimization=0
# The Windows git is a bit older so override the default
GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet
artifacts:
diff --git a/Kconfig.host b/Kconfig.host
index f6a2a131e6..17f405004b 100644
--- a/Kconfig.host
+++ b/Kconfig.host
@@ -23,6 +23,9 @@ config IVSHMEM
config TPM
bool
+config FDT
+ bool
+
config VHOST_USER
bool
diff --git a/MAINTAINERS b/MAINTAINERS
index 302b6fd00c..448dc951c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -167,6 +167,7 @@ F: include/exec/target_long.h
F: include/exec/helper*.h
F: include/exec/helper*.h.inc
F: include/exec/helper-info.c.inc
+F: include/exec/page-protection.h
F: include/sysemu/cpus.h
F: include/sysemu/tcg.h
F: include/hw/core/tcg-cpu-ops.h
@@ -284,7 +285,7 @@ MIPS TCG CPUs
M: Philippe Mathieu-Daudé <philmd@linaro.org>
R: Aurelien Jarno <aurelien@aurel32.net>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
-R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
+R: Aleksandar Rikalo <arikalo@gmail.com>
S: Odd Fixes
F: target/mips/
F: disas/*mips.c
@@ -319,7 +320,7 @@ F: tests/tcg/ppc*/*
RISC-V TCG CPUs
M: Palmer Dabbelt <palmer@dabbelt.com>
M: Alistair Francis <alistair.francis@wdc.com>
-M: Bin Meng <bin.meng@windriver.com>
+M: Bin Meng <bmeng.cn@gmail.com>
R: Weiwei Li <liwei1518@gmail.com>
R: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
R: Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
@@ -532,7 +533,7 @@ Guest CPU Cores (Xen)
---------------------
X86 Xen CPUs
M: Stefano Stabellini <sstabellini@kernel.org>
-M: Anthony Perard <anthony.perard@citrix.com>
+M: Anthony PERARD <anthony@xenproject.org>
M: Paul Durrant <paul@xen.org>
L: xen-devel@lists.xenproject.org
S: Supported
@@ -1241,7 +1242,9 @@ F: configs/devices/loongarch64-softmmu/default.mak
F: hw/loongarch/
F: include/hw/loongarch/virt.h
F: include/hw/intc/loongarch_*.h
+F: include/hw/intc/loongson_ipi.h
F: hw/intc/loongarch_*.c
+F: hw/intc/loongson_ipi.c
F: include/hw/pci-host/ls7a.h
F: hw/rtc/ls7a_rtc.c
F: gdb-xml/loongarch*.xml
@@ -1334,7 +1337,7 @@ F: include/hw/mips/
Jazz
M: Hervé Poussineau <hpoussin@reactos.org>
-R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
+R: Aleksandar Rikalo <arikalo@gmail.com>
S: Maintained
F: hw/mips/jazz.c
F: hw/display/g364fb.c
@@ -1356,7 +1359,7 @@ F: tests/avocado/linux_ssh_mips_malta.py
F: tests/avocado/machine_mips_malta.py
Mipssim
-R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
+R: Aleksandar Rikalo <arikalo@gmail.com>
S: Orphan
F: hw/mips/mipssim.c
F: hw/net/mipsnet.c
@@ -1375,16 +1378,18 @@ Loongson-3 virtual platforms
M: Huacai Chen <chenhuacai@kernel.org>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
S: Maintained
+F: hw/intc/loongson_ipi.c
F: hw/intc/loongson_liointc.c
F: hw/mips/loongson3_bootp.c
F: hw/mips/loongson3_bootp.h
F: hw/mips/loongson3_virt.c
+F: include/hw/intc/loongson_ipi.h
F: include/hw/intc/loongson_liointc.h
F: tests/avocado/machine_mips_loongson3v.py
Boston
M: Paul Burton <paulburton@kernel.org>
-R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
+R: Aleksandar Rikalo <arikalo@gmail.com>
S: Odd Fixes
F: hw/core/loader-fit.c
F: hw/mips/boston.c
@@ -1602,7 +1607,7 @@ F: include/hw/riscv/opentitan.h
F: include/hw/*/ibex_*.h
Microchip PolarFire SoC Icicle Kit
-M: Bin Meng <bin.meng@windriver.com>
+M: Bin Meng <bmeng.cn@gmail.com>
L: qemu-riscv@nongnu.org
S: Supported
F: docs/system/riscv/microchip-icicle-kit.rst
@@ -1629,7 +1634,7 @@ F: include/hw/char/shakti_uart.h
SiFive Machines
M: Alistair Francis <Alistair.Francis@wdc.com>
-M: Bin Meng <bin.meng@windriver.com>
+M: Bin Meng <bmeng.cn@gmail.com>
M: Palmer Dabbelt <palmer@dabbelt.com>
L: qemu-riscv@nongnu.org
S: Supported
@@ -2125,7 +2130,7 @@ F: hw/ssi/xilinx_*
SD (Secure Card)
M: Philippe Mathieu-Daudé <philmd@linaro.org>
-M: Bin Meng <bin.meng@windriver.com>
+M: Bin Meng <bmeng.cn@gmail.com>
L: qemu-block@nongnu.org
S: Odd Fixes
F: include/hw/sd/sd*
@@ -2159,6 +2164,7 @@ F: hw/vfio/*
F: include/hw/vfio/
F: docs/igd-assign.txt
F: docs/devel/migration/vfio.rst
+F: qapi/vfio.json
vfio-ccw
M: Eric Farman <farman@linux.ibm.com>
@@ -2865,7 +2871,6 @@ F: util/aio-*.h
F: util/defer-call.c
F: util/fdmon-*.c
F: block/io.c
-F: migration/block*
F: include/block/aio.h
F: include/block/aio-wait.h
F: include/qemu/defer-call.h
@@ -3692,7 +3697,6 @@ Overall usermode emulation
M: Riku Voipio <riku.voipio@iki.fi>
S: Maintained
F: accel/tcg/user-exec*.c
-F: include/exec/user/
F: include/user/
F: common-user/
@@ -3762,7 +3766,7 @@ M: Philippe Mathieu-Daudé <philmd@linaro.org>
R: Aurelien Jarno <aurelien@aurel32.net>
R: Huacai Chen <chenhuacai@kernel.org>
R: Jiaxun Yang <jiaxun.yang@flygoat.com>
-R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
+R: Aleksandar Rikalo <arikalo@gmail.com>
S: Odd Fixes
F: tcg/mips/
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index d7281b93f3..c0be9f5eed 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -1909,8 +1909,8 @@ void kvm_irqchip_commit_routes(KVMState *s)
assert(ret == 0);
}
-static void kvm_add_routing_entry(KVMState *s,
- struct kvm_irq_routing_entry *entry)
+void kvm_add_routing_entry(KVMState *s,
+ struct kvm_irq_routing_entry *entry)
{
struct kvm_irq_routing_entry *new;
int n, size;
@@ -2007,7 +2007,7 @@ void kvm_irqchip_change_notify(void)
notifier_list_notify(&kvm_irqchip_change_notifiers, NULL);
}
-static int kvm_irqchip_get_virq(KVMState *s)
+int kvm_irqchip_get_virq(KVMState *s)
{
int next_virq;
@@ -2165,62 +2165,6 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event,
return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
}
-int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
-{
- struct kvm_irq_routing_entry kroute = {};
- int virq;
-
- if (!kvm_gsi_routing_enabled()) {
- return -ENOSYS;
- }
-
- virq = kvm_irqchip_get_virq(s);
- if (virq < 0) {
- return virq;
- }
-
- kroute.gsi = virq;
- kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER;
- kroute.flags = 0;
- kroute.u.adapter.summary_addr = adapter->summary_addr;
- kroute.u.adapter.ind_addr = adapter->ind_addr;
- kroute.u.adapter.summary_offset = adapter->summary_offset;
- kroute.u.adapter.ind_offset = adapter->ind_offset;
- kroute.u.adapter.adapter_id = adapter->adapter_id;
-
- kvm_add_routing_entry(s, &kroute);
-
- return virq;
-}
-
-int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint)
-{
- struct kvm_irq_routing_entry kroute = {};
- int virq;
-
- if (!kvm_gsi_routing_enabled()) {
- return -ENOSYS;
- }
- if (!kvm_check_extension(s, KVM_CAP_HYPERV_SYNIC)) {
- return -ENOSYS;
- }
- virq = kvm_irqchip_get_virq(s);
- if (virq < 0) {
- return virq;
- }
-
- kroute.gsi = virq;
- kroute.type = KVM_IRQ_ROUTING_HV_SINT;
- kroute.flags = 0;
- kroute.u.hv_sint.vcpu = vcpu;
- kroute.u.hv_sint.sint = sint;
-
- kvm_add_routing_entry(s, &kroute);
- kvm_irqchip_commit_routes(s);
-
- return virq;
-}
-
#else /* !KVM_CAP_IRQ_ROUTING */
void kvm_init_irq_routing(KVMState *s)
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 225e5fbd3e..2972f75b96 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -147,6 +147,16 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
}
#endif /* CONFIG USER ONLY */
+bool tcg_cflags_has(CPUState *cpu, uint32_t flags)
+{
+ return cpu->tcg_cflags & flags;
+}
+
+void tcg_cflags_set(CPUState *cpu, uint32_t flags)
+{
+ cpu->tcg_cflags |= flags;
+}
+
uint32_t curr_cflags(CPUState *cpu)
{
uint32_t cflags = cpu->tcg_cflags;
@@ -371,7 +381,7 @@ static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc,
* breakpoints are removed.
*/
if (match_page) {
- *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | 1;
+ *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | CF_BP_PAGE | 1;
}
return false;
}
@@ -900,8 +910,6 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
vaddr pc, TranslationBlock **last_tb,
int *tb_exit)
{
- int32_t insns_left;
-
trace_exec_tb(tb, pc);
tb = cpu_tb_exec(cpu, tb, tb_exit);
if (*tb_exit != TB_EXIT_REQUESTED) {
@@ -910,8 +918,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
}
*last_tb = NULL;
- insns_left = qatomic_read(&cpu->neg.icount_decr.u32);
- if (insns_left < 0) {
+ if (cpu_loop_exit_requested(cpu)) {
/* Something asked us to stop executing chained TBs; just
* continue round the main loop. Whatever requested the exit
* will also have set something else (eg exit_request or
@@ -928,7 +935,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
/* Ensure global icount has gone forward */
icount_update(cpu);
/* Refill decrementer and continue execution. */
- insns_left = MIN(0xffff, cpu->icount_budget);
+ int32_t insns_left = MIN(0xffff, cpu->icount_budget);
cpu->neg.icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 953c437ba9..cdb3e12dfb 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -21,6 +21,7 @@
#include "qemu/main-loop.h"
#include "hw/core/tcg-cpu-ops.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/memory.h"
#include "exec/cpu_ldst.h"
#include "exec/cputlb.h"
diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h
index edefd0dcb7..cff43d221b 100644
--- a/accel/tcg/internal-common.h
+++ b/accel/tcg/internal-common.h
@@ -9,6 +9,7 @@
#ifndef ACCEL_TCG_INTERNAL_COMMON_H
#define ACCEL_TCG_INTERNAL_COMMON_H
+#include "exec/cpu-common.h"
#include "exec/translation-block.h"
extern int64_t max_delay;
@@ -20,7 +21,24 @@ extern int64_t max_advance;
*/
static inline bool cpu_in_serial_context(CPUState *cs)
{
- return !(cs->tcg_cflags & CF_PARALLEL) || cpu_in_exclusive_context(cs);
+ return !tcg_cflags_has(cs, CF_PARALLEL) || cpu_in_exclusive_context(cs);
+}
+
+/**
+ * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled?
+ * @cs: CPUState pointer
+ *
+ * The memory callbacks are installed if a plugin has instrumented an
+ * instruction for memory. This can be useful to know if you want to
+ * force a slow path for a series of memory accesses.
+ */
+static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu)
+{
+#ifdef CONFIG_PLUGIN
+ return !!cpu->neg.plugin_mem_cbs;
+#else
+ return false;
+#endif
}
#endif
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index cd78ef94a1..cc1634e7a6 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -14,36 +14,14 @@
* Injecting the desired instrumentation could be done with a second
* translation pass that combined the instrumentation requests, but that
* would be ugly and inefficient since we would decode the guest code twice.
- * Instead, during TB translation we add "empty" instrumentation calls for all
- * possible instrumentation events, and then once we collect the instrumentation
- * requests from plugins, we either "fill in" those empty events or remove them
- * if they have no requests.
- *
- * When "filling in" an event we first copy the empty callback's TCG ops. This
- * might seem unnecessary, but it is done to support an arbitrary number
- * of callbacks per event. Take for example a regular instruction callback.
- * We first generate a callback to an empty helper function. Then, if two
- * plugins register one callback each for this instruction, we make two copies
- * of the TCG ops generated for the empty callback, substituting the function
- * pointer that points to the empty helper function with the plugins' desired
- * callback functions. After that we remove the empty callback's ops.
- *
- * Note that the location in TCGOp.args[] of the pointer to a helper function
- * varies across different guest and host architectures. Instead of duplicating
- * the logic that figures this out, we rely on the fact that the empty
- * callbacks point to empty functions that are unique pointers in the program.
- * Thus, to find the right location we just have to look for a match in
- * TCGOp.args[]. This is the main reason why we first copy an empty callback's
- * TCG ops and then fill them in; regardless of whether we have one or many
- * callbacks for that event, the logic to add all of them is the same.
- *
- * When generating more than one callback per event, we make a small
- * optimization to avoid generating redundant operations. For instance, for the
- * second and all subsequent callbacks of an event, we do not need to reload the
- * CPU's index into a TCG temp, since the first callback did it already.
+ * Instead, during TB translation we add "plugin_cb" marker opcodes
+ * for all possible instrumentation events, and then once we collect the
+ * instrumentation requests from plugins, we generate code for those markers
+ * or remove them if they have no requests.
*/
#include "qemu/osdep.h"
#include "qemu/plugin.h"
+#include "qemu/log.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "tcg/tcg-temp-internal.h"
@@ -51,885 +29,425 @@
#include "exec/exec-all.h"
#include "exec/plugin-gen.h"
#include "exec/translator.h"
-#include "exec/helper-proto-common.h"
-
-#define HELPER_H "accel/tcg/plugin-helpers.h"
-#include "exec/helper-info.c.inc"
-#undef HELPER_H
-
-/*
- * plugin_cb_start TCG op args[]:
- * 0: enum plugin_gen_from
- * 1: enum plugin_gen_cb
- * 2: set to 1 for mem callback that is a write, 0 otherwise.
- */
enum plugin_gen_from {
PLUGIN_GEN_FROM_TB,
PLUGIN_GEN_FROM_INSN,
- PLUGIN_GEN_FROM_MEM,
PLUGIN_GEN_AFTER_INSN,
- PLUGIN_GEN_N_FROMS,
-};
-
-enum plugin_gen_cb {
- PLUGIN_GEN_CB_UDATA,
- PLUGIN_GEN_CB_UDATA_R,
- PLUGIN_GEN_CB_INLINE,
- PLUGIN_GEN_CB_MEM,
- PLUGIN_GEN_ENABLE_MEM_HELPER,
- PLUGIN_GEN_DISABLE_MEM_HELPER,
- PLUGIN_GEN_N_CBS,
+ PLUGIN_GEN_AFTER_TB,
};
-/*
- * These helpers are stubs that get dynamically switched out for calls
- * direct to the plugin if they are subscribed to.
- */
-void HELPER(plugin_vcpu_udata_cb_no_wg)(uint32_t cpu_index, void *udata)
-{ }
-
-void HELPER(plugin_vcpu_udata_cb_no_rwg)(uint32_t cpu_index, void *udata)
-{ }
-
-void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
- qemu_plugin_meminfo_t info, uint64_t vaddr,
- void *userdata)
-{ }
+/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
+void plugin_gen_disable_mem_helpers(void)
+{
+ if (tcg_ctx->plugin_insn) {
+ tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB);
+ }
+}
-static void gen_empty_udata_cb(void (*gen_helper)(TCGv_i32, TCGv_ptr))
+static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
+ struct qemu_plugin_insn *insn)
{
- TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
- TCGv_ptr udata = tcg_temp_ebb_new_ptr();
+ GArray *arr;
+ size_t len;
- tcg_gen_movi_ptr(udata, 0);
- tcg_gen_ld_i32(cpu_index, tcg_env,
- -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
- gen_helper(cpu_index, udata);
+ /*
+ * Tracking memory accesses performed from helpers requires extra work.
+ * If an instruction is emulated with helpers, we do two things:
+ * (1) copy the CB descriptors, and keep track of it so that they can be
+ * freed later on, and (2) point CPUState.neg.plugin_mem_cbs to the
+ * descriptors, so that we can read them at run-time
+ * (i.e. when the helper executes).
+ * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
+ *
+ * Note that plugin_gen_disable_mem_helpers undoes (2). Since it
+ * is possible that the code we generate after the instruction is
+ * dead, we also add checks before generating tb_exit etc.
+ */
+ if (!insn->calls_helpers) {
+ return;
+ }
- tcg_temp_free_ptr(udata);
- tcg_temp_free_i32(cpu_index);
-}
+ if (!insn->mem_cbs || !insn->mem_cbs->len) {
+ insn->mem_helper = false;
+ return;
+ }
+ insn->mem_helper = true;
+ ptb->mem_helper = true;
-static void gen_empty_udata_cb_no_wg(void)
-{
- gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_wg);
+ /*
+ * TODO: It seems like we should be able to use ref/unref
+ * to avoid needing to actually copy this array.
+ * Alternately, perhaps we could allocate new memory adjacent
+ * to the TranslationBlock itself, so that we do not have to
+ * actively manage the lifetime after this.
+ */
+ len = insn->mem_cbs->len;
+ arr = g_array_sized_new(false, false,
+ sizeof(struct qemu_plugin_dyn_cb), len);
+ memcpy(arr->data, insn->mem_cbs->data,
+ len * sizeof(struct qemu_plugin_dyn_cb));
+ qemu_plugin_add_dyn_cb_arr(arr);
+
+ tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env,
+ offsetof(CPUState, neg.plugin_mem_cbs) -
+ offsetof(ArchCPU, env));
}
-static void gen_empty_udata_cb_no_rwg(void)
+static void gen_disable_mem_helper(void)
{
- gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_rwg);
+ tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
+ offsetof(CPUState, neg.plugin_mem_cbs) -
+ offsetof(ArchCPU, env));
}
-/*
- * For now we only support addi_i64.
- * When we support more ops, we can generate one empty inline cb for each.
- */
-static void gen_empty_inline_cb(void)
+static TCGv_i32 gen_cpu_index(void)
{
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
- TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr();
- TCGv_i64 val = tcg_temp_ebb_new_i64();
- TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
-
tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
- /* second operand will be replaced by immediate value */
- tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index);
- tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index);
-
- tcg_gen_movi_ptr(ptr, 0);
- tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr);
- tcg_gen_ld_i64(val, ptr, 0);
- /* second operand will be replaced by immediate value */
- tcg_gen_add_i64(val, val, val);
-
- tcg_gen_st_i64(val, ptr, 0);
- tcg_temp_free_ptr(ptr);
- tcg_temp_free_i64(val);
- tcg_temp_free_ptr(cpu_index_as_ptr);
- tcg_temp_free_i32(cpu_index);
+ return cpu_index;
}
-static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
+static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
{
- TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
- TCGv_i32 meminfo = tcg_temp_ebb_new_i32();
- TCGv_ptr udata = tcg_temp_ebb_new_ptr();
-
- tcg_gen_movi_i32(meminfo, info);
- tcg_gen_movi_ptr(udata, 0);
- tcg_gen_ld_i32(cpu_index, tcg_env,
- -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-
- gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata);
-
- tcg_temp_free_ptr(udata);
- tcg_temp_free_i32(meminfo);
+ TCGv_i32 cpu_index = gen_cpu_index();
+ tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
+ tcgv_i32_temp(cpu_index),
+ tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
tcg_temp_free_i32(cpu_index);
}
-/*
- * Share the same function for enable/disable. When enabling, the NULL
- * pointer will be overwritten later.
- */
-static void gen_empty_mem_helper(void)
+static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
{
TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
- tcg_gen_movi_ptr(ptr, 0);
- tcg_gen_st_ptr(ptr, tcg_env, offsetof(CPUState, plugin_mem_cbs) -
- offsetof(ArchCPU, env));
- tcg_temp_free_ptr(ptr);
-}
-
-static void gen_plugin_cb_start(enum plugin_gen_from from,
- enum plugin_gen_cb type, unsigned wr)
-{
- tcg_gen_plugin_cb_start(from, type, wr);
-}
-
-static void gen_wrapped(enum plugin_gen_from from,
- enum plugin_gen_cb type, void (*func)(void))
-{
- gen_plugin_cb_start(from, type, 0);
- func();
- tcg_gen_plugin_cb_end();
-}
+ GArray *arr = entry.score->data;
+ char *base_ptr = arr->data + entry.offset;
+ size_t entry_size = g_array_get_element_size(arr);
-static void plugin_gen_empty_callback(enum plugin_gen_from from)
-{
- switch (from) {
- case PLUGIN_GEN_AFTER_INSN:
- gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
- gen_empty_mem_helper);
- break;
- case PLUGIN_GEN_FROM_INSN:
- /*
- * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being
- * the first callback of an instruction
- */
- gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
- gen_empty_mem_helper);
- /* fall through */
- case PLUGIN_GEN_FROM_TB:
- gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg);
- gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg);
- gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
- break;
+ TCGv_i32 cpu_index = gen_cpu_index();
+ tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
+ tcg_gen_ext_i32_ptr(ptr, cpu_index);
+ tcg_temp_free_i32(cpu_index);
+ tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr);
+
+ return ptr;
+}
+
+static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond)
+{
+ switch (cond) {
+ case QEMU_PLUGIN_COND_EQ:
+ return TCG_COND_EQ;
+ case QEMU_PLUGIN_COND_NE:
+ return TCG_COND_NE;
+ case QEMU_PLUGIN_COND_LT:
+ return TCG_COND_LTU;
+ case QEMU_PLUGIN_COND_LE:
+ return TCG_COND_LEU;
+ case QEMU_PLUGIN_COND_GT:
+ return TCG_COND_GTU;
+ case QEMU_PLUGIN_COND_GE:
+ return TCG_COND_GEU;
default:
+ /* ALWAYS and NEVER conditions should never reach */
g_assert_not_reached();
}
}
-void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
-{
- enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
-
- gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw);
- gen_empty_mem_cb(addr, info);
- tcg_gen_plugin_cb_end();
-
- gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw);
- gen_empty_inline_cb();
- tcg_gen_plugin_cb_end();
-}
-
-static TCGOp *find_op(TCGOp *op, TCGOpcode opc)
-{
- while (op) {
- if (op->opc == opc) {
- return op;
- }
- op = QTAILQ_NEXT(op, link);
- }
- return NULL;
-}
-
-static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end)
-{
- TCGOp *ret = QTAILQ_NEXT(end, link);
-
- QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link);
- return ret;
-}
-
-/* remove all ops until (and including) plugin_cb_end */
-static TCGOp *rm_ops(TCGOp *op)
-{
- TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end);
-
- tcg_debug_assert(end_op);
- return rm_ops_range(op, end_op);
-}
-
-static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
-{
- TCGOp *old_op = QTAILQ_NEXT(*begin_op, link);
- unsigned nargs = old_op->nargs;
-
- *begin_op = old_op;
- op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs);
- memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs);
-
- return op;
-}
-
-static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc)
-{
- op = copy_op_nocheck(begin_op, op);
- tcg_debug_assert((*begin_op)->opc == opc);
- return op;
-}
-
-static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
-{
- if (UINTPTR_MAX == UINT32_MAX) {
- /* mov_i32 */
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- op->args[1] = tcgv_i32_arg(tcg_constant_i32((uintptr_t)ptr));
- } else {
- /* mov_i64 */
- op = copy_op(begin_op, op, INDEX_op_mov_i64);
- op->args[1] = tcgv_i64_arg(tcg_constant_i64((uintptr_t)ptr));
- }
- return op;
-}
-
-static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op)
-{
- return copy_op(begin_op, op, INDEX_op_ld_i32);
-}
-
-static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op)
+static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
{
- if (UINTPTR_MAX == UINT32_MAX) {
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- } else {
- op = copy_op(begin_op, op, INDEX_op_ext_i32_i64);
- }
- return op;
-}
+ TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
+ TCGv_i64 val = tcg_temp_ebb_new_i64();
+ TCGLabel *after_cb = gen_new_label();
-static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op)
-{
- if (UINTPTR_MAX == UINT32_MAX) {
- op = copy_op(begin_op, op, INDEX_op_add_i32);
- } else {
- op = copy_op(begin_op, op, INDEX_op_add_i64);
- }
- return op;
-}
+ /* Condition should be negated, as calling the cb is the "else" path */
+ TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond));
-static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
-{
- if (TCG_TARGET_REG_BITS == 32) {
- /* 2x ld_i32 */
- op = copy_ld_i32(begin_op, op);
- op = copy_ld_i32(begin_op, op);
- } else {
- /* ld_i64 */
- op = copy_op(begin_op, op, INDEX_op_ld_i64);
- }
- return op;
-}
+ tcg_gen_ld_i64(val, ptr, 0);
+ tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
+ TCGv_i32 cpu_index = gen_cpu_index();
+ tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
+ tcgv_i32_temp(cpu_index),
+ tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+ tcg_temp_free_i32(cpu_index);
+ gen_set_label(after_cb);
-static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op)
-{
- if (TCG_TARGET_REG_BITS == 32) {
- /* 2x st_i32 */
- op = copy_op(begin_op, op, INDEX_op_st_i32);
- op = copy_op(begin_op, op, INDEX_op_st_i32);
- } else {
- /* st_i64 */
- op = copy_op(begin_op, op, INDEX_op_st_i64);
- }
- return op;
+ tcg_temp_free_i64(val);
+ tcg_temp_free_ptr(ptr);
}
-static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
+static void gen_inline_add_u64_cb(struct qemu_plugin_inline_cb *cb)
{
- if (TCG_TARGET_REG_BITS == 32) {
- /* all 32-bit backends must implement add2_i32 */
- g_assert(TCG_TARGET_HAS_add2_i32);
- op = copy_op(begin_op, op, INDEX_op_add2_i32);
- op->args[4] = tcgv_i32_arg(tcg_constant_i32(v));
- op->args[5] = tcgv_i32_arg(tcg_constant_i32(v >> 32));
- } else {
- op = copy_op(begin_op, op, INDEX_op_add_i64);
- op->args[2] = tcgv_i64_arg(tcg_constant_i64(v));
- }
- return op;
-}
+ TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
+ TCGv_i64 val = tcg_temp_ebb_new_i64();
-static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v)
-{
- op = copy_op(begin_op, op, INDEX_op_mul_i32);
- op->args[2] = tcgv_i32_arg(tcg_constant_i32(v));
- return op;
-}
+ tcg_gen_ld_i64(val, ptr, 0);
+ tcg_gen_addi_i64(val, val, cb->imm);
+ tcg_gen_st_i64(val, ptr, 0);
-static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
-{
- if (UINTPTR_MAX == UINT32_MAX) {
- /* st_i32 */
- op = copy_op(begin_op, op, INDEX_op_st_i32);
- } else {
- /* st_i64 */
- op = copy_st_i64(begin_op, op);
- }
- return op;
+ tcg_temp_free_i64(val);
+ tcg_temp_free_ptr(ptr);
}
-static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx)
+static void gen_inline_store_u64_cb(struct qemu_plugin_inline_cb *cb)
{
- TCGOp *old_op;
- int func_idx;
-
- /* copy all ops until the call */
- do {
- op = copy_op_nocheck(begin_op, op);
- } while (op->opc != INDEX_op_call);
+ TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
+ TCGv_i64 val = tcg_constant_i64(cb->imm);
- /* fill in the op call */
- old_op = *begin_op;
- TCGOP_CALLI(op) = TCGOP_CALLI(old_op);
- TCGOP_CALLO(op) = TCGOP_CALLO(old_op);
- tcg_debug_assert(op->life == 0);
-
- func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op);
- *cb_idx = func_idx;
- op->args[func_idx] = (uintptr_t)func;
+ tcg_gen_st_i64(val, ptr, 0);
- return op;
+ tcg_temp_free_ptr(ptr);
}
-/*
- * When we append/replace ops here we are sensitive to changing patterns of
- * TCGOps generated by the tcg_gen_FOO calls when we generated the
- * empty callbacks. This will assert very quickly in a debug build as
- * we assert the ops we are replacing are the correct ones.
- */
-static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
- TCGOp *begin_op, TCGOp *op, int *cb_idx)
+static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
+ qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
{
- /* const_ptr */
- op = copy_const_ptr(&begin_op, op, cb->userp);
-
- /* copy the ld_i32, but note that we only have to copy it once */
- if (*cb_idx == -1) {
- op = copy_op(&begin_op, op, INDEX_op_ld_i32);
- } else {
- begin_op = QTAILQ_NEXT(begin_op, link);
- tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
- }
-
- /* call */
- op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx);
-
- return op;
+ TCGv_i32 cpu_index = gen_cpu_index();
+ tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
+ tcgv_i32_temp(cpu_index),
+ tcgv_i32_temp(tcg_constant_i32(meminfo)),
+ tcgv_i64_temp(addr),
+ tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+ tcg_temp_free_i32(cpu_index);
}
-static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
- TCGOp *begin_op, TCGOp *op,
- int *unused)
-{
- char *ptr = cb->inline_insn.entry.score->data->data;
- size_t elem_size = g_array_get_element_size(
- cb->inline_insn.entry.score->data);
- size_t offset = cb->inline_insn.entry.offset;
-
- op = copy_ld_i32(&begin_op, op);
- op = copy_mul_i32(&begin_op, op, elem_size);
- op = copy_ext_i32_ptr(&begin_op, op);
- op = copy_const_ptr(&begin_op, op, ptr + offset);
- op = copy_add_ptr(&begin_op, op);
- op = copy_ld_i64(&begin_op, op);
- op = copy_add_i64(&begin_op, op, cb->inline_insn.imm);
- op = copy_st_i64(&begin_op, op);
- return op;
-}
+static void inject_cb(struct qemu_plugin_dyn_cb *cb)
-static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
- TCGOp *begin_op, TCGOp *op, int *cb_idx)
{
- enum plugin_gen_cb type = begin_op->args[1];
-
- tcg_debug_assert(type == PLUGIN_GEN_CB_MEM);
-
- /* const_i32 == mov_i32 ("info", so it remains as is) */
- op = copy_op(&begin_op, op, INDEX_op_mov_i32);
-
- /* const_ptr */
- op = copy_const_ptr(&begin_op, op, cb->userp);
-
- /* copy the ld_i32, but note that we only have to copy it once */
- if (*cb_idx == -1) {
- op = copy_op(&begin_op, op, INDEX_op_ld_i32);
- } else {
- begin_op = QTAILQ_NEXT(begin_op, link);
- tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
- }
-
- if (type == PLUGIN_GEN_CB_MEM) {
- /* call */
- op = copy_call(&begin_op, op, cb->f.vcpu_udata, cb_idx);
+ switch (cb->type) {
+ case PLUGIN_CB_REGULAR:
+ gen_udata_cb(&cb->regular);
+ break;
+ case PLUGIN_CB_COND:
+ gen_udata_cond_cb(&cb->cond);
+ break;
+ case PLUGIN_CB_INLINE_ADD_U64:
+ gen_inline_add_u64_cb(&cb->inline_insn);
+ break;
+ case PLUGIN_CB_INLINE_STORE_U64:
+ gen_inline_store_u64_cb(&cb->inline_insn);
+ break;
+ default:
+ g_assert_not_reached();
}
-
- return op;
}
-typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb,
- TCGOp *begin_op, TCGOp *op, int *intp);
-typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb);
-
-static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
-{
- return true;
-}
-
-static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
+static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb,
+ enum qemu_plugin_mem_rw rw,
+ qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
{
- int w;
-
- w = op->args[2];
- return !!(cb->rw & (w + 1));
-}
-
-static void inject_cb_type(const GArray *cbs, TCGOp *begin_op,
- inject_fn inject, op_ok_fn ok)
-{
- TCGOp *end_op;
- TCGOp *op;
- int cb_idx = -1;
- int i;
-
- if (!cbs || cbs->len == 0) {
- rm_ops(begin_op);
- return;
- }
-
- end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
- tcg_debug_assert(end_op);
-
- op = end_op;
- for (i = 0; i < cbs->len; i++) {
- struct qemu_plugin_dyn_cb *cb =
- &g_array_index(cbs, struct qemu_plugin_dyn_cb, i);
-
- if (!ok(begin_op, cb)) {
- continue;
+ switch (cb->type) {
+ case PLUGIN_CB_MEM_REGULAR:
+ if (rw && cb->regular.rw) {
+ gen_mem_cb(&cb->regular, meminfo, addr);
}
- op = inject(cb, begin_op, op, &cb_idx);
+ break;
+ case PLUGIN_CB_INLINE_ADD_U64:
+ case PLUGIN_CB_INLINE_STORE_U64:
+ if (rw && cb->inline_insn.rw) {
+ inject_cb(cb);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ break;
}
- rm_ops_range(begin_op, end_op);
-}
-
-static void
-inject_udata_cb(const GArray *cbs, TCGOp *begin_op)
-{
- inject_cb_type(cbs, begin_op, append_udata_cb, op_ok);
-}
-
-static void
-inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok)
-{
- inject_cb_type(cbs, begin_op, append_inline_cb, ok);
-}
-
-static void
-inject_mem_cb(const GArray *cbs, TCGOp *begin_op)
-{
- inject_cb_type(cbs, begin_op, append_mem_cb, op_rw);
-}
-
-/* we could change the ops in place, but we can reuse more code by copying */
-static void inject_mem_helper(TCGOp *begin_op, GArray *arr)
-{
- TCGOp *orig_op = begin_op;
- TCGOp *end_op;
- TCGOp *op;
-
- end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
- tcg_debug_assert(end_op);
-
- /* const ptr */
- op = copy_const_ptr(&begin_op, end_op, arr);
-
- /* st_ptr */
- op = copy_st_ptr(&begin_op, op);
-
- rm_ops_range(orig_op, end_op);
}
-/*
- * Tracking memory accesses performed from helpers requires extra work.
- * If an instruction is emulated with helpers, we do two things:
- * (1) copy the CB descriptors, and keep track of it so that they can be
- * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so
- * that we can read them at run-time (i.e. when the helper executes).
- * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
- *
- * Note that plugin_gen_disable_mem_helpers undoes (2). Since it
- * is possible that the code we generate after the instruction is
- * dead, we also add checks before generating tb_exit etc.
- */
-static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb,
- struct qemu_plugin_insn *plugin_insn,
- TCGOp *begin_op)
+static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
{
- GArray *cbs[2];
- GArray *arr;
- size_t n_cbs, i;
-
- cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
- cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
-
- n_cbs = 0;
- for (i = 0; i < ARRAY_SIZE(cbs); i++) {
- n_cbs += cbs[i]->len;
- }
-
- plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs;
- if (likely(!plugin_insn->mem_helper)) {
- rm_ops(begin_op);
- return;
- }
- ptb->mem_helper = true;
-
- arr = g_array_sized_new(false, false,
- sizeof(struct qemu_plugin_dyn_cb), n_cbs);
-
- for (i = 0; i < ARRAY_SIZE(cbs); i++) {
- g_array_append_vals(arr, cbs[i]->data, cbs[i]->len);
- }
-
- qemu_plugin_add_dyn_cb_arr(arr);
- inject_mem_helper(begin_op, arr);
-}
+ TCGOp *op, *next;
+ int insn_idx = -1;
-static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
- TCGOp *begin_op)
-{
- if (likely(!plugin_insn->mem_helper)) {
- rm_ops(begin_op);
- return;
+ if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN)
+ && qemu_log_in_addr_range(tcg_ctx->plugin_db->pc_first))) {
+ FILE *logfile = qemu_log_trylock();
+ if (logfile) {
+ fprintf(logfile, "OP before plugin injection:\n");
+ tcg_dump_ops(tcg_ctx, logfile, false);
+ fprintf(logfile, "\n");
+ qemu_log_unlock(logfile);
+ }
}
- inject_mem_helper(begin_op, NULL);
-}
-/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
-void plugin_gen_disable_mem_helpers(void)
-{
/*
- * We could emit the clearing unconditionally and be done. However, this can
- * be wasteful if for instance plugins don't track memory accesses, or if
- * most TBs don't use helpers. Instead, emit the clearing iff the TB calls
- * helpers that might access guest memory.
- *
- * Note: we do not reset plugin_tb->mem_helper here; a TB might have several
- * exit points, and we want to emit the clearing from all of them.
+ * While injecting code, we cannot afford to reuse any ebb temps
+ * that might be live within the existing opcode stream.
+ * The simplest solution is to release them all and create new.
*/
- if (!tcg_ctx->plugin_tb->mem_helper) {
- return;
- }
- tcg_gen_st_ptr(tcg_constant_ptr(NULL), tcg_env,
- offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env));
-}
-
-static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op)
-{
- inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
-}
-
-static void plugin_gen_tb_udata_r(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op)
-{
- inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR_R], begin_op);
-}
-
-static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op)
-{
- inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok);
-}
-
-static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
-
- inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
-}
-
-static void plugin_gen_insn_udata_r(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
-
- inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR_R], begin_op);
-}
-
-static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
- begin_op, op_ok);
-}
-
-static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op);
-}
-
-static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- const GArray *cbs;
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
-
- cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
- inject_inline_cb(cbs, begin_op, op_rw);
-}
-
-static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_mem_enable_helper(ptb, insn, begin_op);
-}
-
-static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
- TCGOp *begin_op, int insn_idx)
-{
- struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_mem_disable_helper(insn, begin_op);
-}
-
-/* #define DEBUG_PLUGIN_GEN_OPS */
-static void pr_ops(void)
-{
-#ifdef DEBUG_PLUGIN_GEN_OPS
- TCGOp *op;
- int i = 0;
-
- QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
- const char *name = "";
- const char *type = "";
-
- if (op->opc == INDEX_op_plugin_cb_start) {
- switch (op->args[0]) {
- case PLUGIN_GEN_FROM_TB:
- name = "tb";
- break;
- case PLUGIN_GEN_FROM_INSN:
- name = "insn";
- break;
- case PLUGIN_GEN_FROM_MEM:
- name = "mem";
- break;
- case PLUGIN_GEN_AFTER_INSN:
- name = "after insn";
- break;
- default:
- break;
- }
- switch (op->args[1]) {
- case PLUGIN_GEN_CB_UDATA:
- type = "udata";
- break;
- case PLUGIN_GEN_CB_INLINE:
- type = "inline";
- break;
- case PLUGIN_GEN_CB_MEM:
- type = "mem";
- break;
- case PLUGIN_GEN_ENABLE_MEM_HELPER:
- type = "enable mem helper";
- break;
- case PLUGIN_GEN_DISABLE_MEM_HELPER:
- type = "disable mem helper";
- break;
- default:
- break;
- }
- }
- printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type);
- i++;
- }
-#endif
-}
-
-static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
-{
- TCGOp *op;
- int insn_idx = -1;
+ memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps));
- pr_ops();
-
- QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
+ QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) {
switch (op->opc) {
case INDEX_op_insn_start:
insn_idx++;
break;
- case INDEX_op_plugin_cb_start:
+
+ case INDEX_op_plugin_cb:
{
enum plugin_gen_from from = op->args[0];
- enum plugin_gen_cb type = op->args[1];
+ struct qemu_plugin_insn *insn = NULL;
+ const GArray *cbs;
+ int i, n;
+
+ if (insn_idx >= 0) {
+ insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
+ }
+
+ tcg_ctx->emit_before_op = op;
switch (from) {
- case PLUGIN_GEN_FROM_TB:
- {
- g_assert(insn_idx == -1);
-
- switch (type) {
- case PLUGIN_GEN_CB_UDATA:
- plugin_gen_tb_udata(plugin_tb, op);
- break;
- case PLUGIN_GEN_CB_UDATA_R:
- plugin_gen_tb_udata_r(plugin_tb, op);
- break;
- case PLUGIN_GEN_CB_INLINE:
- plugin_gen_tb_inline(plugin_tb, op);
- break;
- default:
- g_assert_not_reached();
+ case PLUGIN_GEN_AFTER_TB:
+ if (plugin_tb->mem_helper) {
+ gen_disable_mem_helper();
}
break;
- }
- case PLUGIN_GEN_FROM_INSN:
- {
- g_assert(insn_idx >= 0);
-
- switch (type) {
- case PLUGIN_GEN_CB_UDATA:
- plugin_gen_insn_udata(plugin_tb, op, insn_idx);
- break;
- case PLUGIN_GEN_CB_UDATA_R:
- plugin_gen_insn_udata_r(plugin_tb, op, insn_idx);
- break;
- case PLUGIN_GEN_CB_INLINE:
- plugin_gen_insn_inline(plugin_tb, op, insn_idx);
- break;
- case PLUGIN_GEN_ENABLE_MEM_HELPER:
- plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx);
- break;
- default:
- g_assert_not_reached();
+
+ case PLUGIN_GEN_AFTER_INSN:
+ assert(insn != NULL);
+ if (insn->mem_helper) {
+ gen_disable_mem_helper();
}
break;
- }
- case PLUGIN_GEN_FROM_MEM:
- {
- g_assert(insn_idx >= 0);
-
- switch (type) {
- case PLUGIN_GEN_CB_MEM:
- plugin_gen_mem_regular(plugin_tb, op, insn_idx);
- break;
- case PLUGIN_GEN_CB_INLINE:
- plugin_gen_mem_inline(plugin_tb, op, insn_idx);
- break;
- default:
- g_assert_not_reached();
- }
+ case PLUGIN_GEN_FROM_TB:
+ assert(insn == NULL);
+
+ cbs = plugin_tb->cbs;
+ for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
+ inject_cb(
+ &g_array_index(cbs, struct qemu_plugin_dyn_cb, i));
+ }
break;
- }
- case PLUGIN_GEN_AFTER_INSN:
- {
- g_assert(insn_idx >= 0);
-
- switch (type) {
- case PLUGIN_GEN_DISABLE_MEM_HELPER:
- plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx);
- break;
- default:
- g_assert_not_reached();
+
+ case PLUGIN_GEN_FROM_INSN:
+ assert(insn != NULL);
+
+ gen_enable_mem_helper(plugin_tb, insn);
+
+ cbs = insn->insn_cbs;
+ for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
+ inject_cb(
+ &g_array_index(cbs, struct qemu_plugin_dyn_cb, i));
}
break;
- }
+
default:
g_assert_not_reached();
}
+
+ tcg_ctx->emit_before_op = NULL;
+ tcg_op_remove(tcg_ctx, op);
break;
}
+
+ case INDEX_op_plugin_mem_cb:
+ {
+ TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0]));
+ qemu_plugin_meminfo_t meminfo = op->args[1];
+ enum qemu_plugin_mem_rw rw =
+ (qemu_plugin_mem_is_store(meminfo)
+ ? QEMU_PLUGIN_MEM_W : QEMU_PLUGIN_MEM_R);
+ struct qemu_plugin_insn *insn;
+ const GArray *cbs;
+ int i, n;
+
+ assert(insn_idx >= 0);
+ insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
+
+ tcg_ctx->emit_before_op = op;
+
+ cbs = insn->mem_cbs;
+ for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
+ inject_mem_cb(&g_array_index(cbs, struct qemu_plugin_dyn_cb, i),
+ rw, meminfo, addr);
+ }
+
+ tcg_ctx->emit_before_op = NULL;
+ tcg_op_remove(tcg_ctx, op);
+ break;
+ }
+
default:
/* plugins don't care about any other ops */
break;
}
}
- pr_ops();
}
-bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
- bool mem_only)
+bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db)
{
- bool ret = false;
+ struct qemu_plugin_tb *ptb;
- if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) {
- struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
- int i;
+ if (!test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS,
+ cpu->plugin_state->event_mask)) {
+ return false;
+ }
- /* reset callbacks */
- for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
- if (ptb->cbs[i]) {
- g_array_set_size(ptb->cbs[i], 0);
- }
+ tcg_ctx->plugin_db = db;
+ tcg_ctx->plugin_insn = NULL;
+ ptb = tcg_ctx->plugin_tb;
+
+ if (ptb) {
+ /* Reset callbacks */
+ if (ptb->cbs) {
+ g_array_set_size(ptb->cbs, 0);
}
ptb->n = 0;
-
- ret = true;
-
- ptb->vaddr = db->pc_first;
- ptb->vaddr2 = -1;
- ptb->haddr1 = db->host_addr[0];
- ptb->haddr2 = NULL;
- ptb->mem_only = mem_only;
ptb->mem_helper = false;
-
- plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
+ } else {
+ ptb = g_new0(struct qemu_plugin_tb, 1);
+ tcg_ctx->plugin_tb = ptb;
+ ptb->insns = g_ptr_array_new();
}
- tcg_ctx->plugin_insn = NULL;
-
- return ret;
+ tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB);
+ return true;
}
void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
- struct qemu_plugin_insn *pinsn;
-
- pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next);
- tcg_ctx->plugin_insn = pinsn;
- plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
-
- /*
- * Detect page crossing to get the new host address.
- * Note that we skip this when haddr1 == NULL, e.g. when we're
- * fetching instructions from a region not backed by RAM.
- */
- if (ptb->haddr1 == NULL) {
- pinsn->haddr = NULL;
- } else if (is_same_page(db, db->pc_next)) {
- pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
+ struct qemu_plugin_insn *insn;
+ size_t n = db->num_insns;
+ vaddr pc;
+
+ assert(n >= 1);
+ ptb->n = n;
+ if (n <= ptb->insns->len) {
+ insn = g_ptr_array_index(ptb->insns, n - 1);
} else {
- if (ptb->vaddr2 == -1) {
- ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
- get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2);
- }
- pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
+ assert(n - 1 == ptb->insns->len);
+ insn = g_new0(struct qemu_plugin_insn, 1);
+ g_ptr_array_add(ptb->insns, insn);
+ }
+
+ tcg_ctx->plugin_insn = insn;
+ insn->calls_helpers = false;
+ insn->mem_helper = false;
+ if (insn->insn_cbs) {
+ g_array_set_size(insn->insn_cbs, 0);
}
+ if (insn->mem_cbs) {
+ g_array_set_size(insn->mem_cbs, 0);
+ }
+
+ pc = db->pc_next;
+ insn->vaddr = pc;
+
+ tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN);
}
void plugin_gen_insn_end(void)
{
- plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
+ const DisasContextBase *db = tcg_ctx->plugin_db;
+ struct qemu_plugin_insn *pinsn = tcg_ctx->plugin_insn;
+
+ pinsn->len = db->fake_insn ? db->record_len : db->pc_next - pinsn->vaddr;
+
+ tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_INSN);
}
/*
diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h
deleted file mode 100644
index 11796436f3..0000000000
--- a/accel/tcg/plugin-helpers.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifdef CONFIG_PLUGIN
-DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_wg, TCG_CALL_NO_WG | TCG_CALL_PLUGIN, void, i32, ptr)
-DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_rwg, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, ptr)
-DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, void, i32, i32, i64, ptr)
-#endif
diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h
index 184bb3e3e2..c3a505e394 100644
--- a/accel/tcg/tb-jmp-cache.h
+++ b/accel/tcg/tb-jmp-cache.h
@@ -22,12 +22,12 @@
* non-NULL value of 'tb'. Strictly speaking pc is only needed for
* CF_PCREL, but it's used always for simplicity.
*/
-struct CPUJumpCache {
+typedef struct CPUJumpCache {
struct rcu_head rcu;
struct {
TranslationBlock *tb;
vaddr pc;
} array[TB_JMP_CACHE_SIZE];
-};
+} CPUJumpCache;
#endif /* ACCEL_TCG_TB_JMP_CACHE_H */
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
index da39a43bd8..19ae6793f3 100644
--- a/accel/tcg/tb-maint.c
+++ b/accel/tcg/tb-maint.c
@@ -23,6 +23,7 @@
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/tb-flush.h"
#include "exec/translate-all.h"
#include "sysemu/tcg.h"
diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c
index 2c7b0cc09e..1433e38f40 100644
--- a/accel/tcg/tcg-accel-ops.c
+++ b/accel/tcg/tcg-accel-ops.c
@@ -62,7 +62,7 @@ void tcg_cpu_init_cflags(CPUState *cpu, bool parallel)
cflags |= parallel ? CF_PARALLEL : 0;
cflags |= icount_enabled() ? CF_USE_ICOUNT : 0;
- cpu->tcg_cflags |= cflags;
+ tcg_cflags_set(cpu, cflags);
}
void tcg_cpu_destroy(CPUState *cpu)
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 83cc14fbde..fdf6d8ac19 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -644,15 +644,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
cpu_loop_exit_noexc(cpu);
}
-#else /* CONFIG_USER_ONLY */
-
-void cpu_interrupt(CPUState *cpu, int mask)
-{
- g_assert(bql_locked());
- cpu->interrupt_request |= mask;
- qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
-}
-
#endif /* CONFIG_USER_ONLY */
/*
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index 6832e55135..c56967eecd 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -14,8 +14,10 @@
#include "exec/translator.h"
#include "exec/cpu_ldst.h"
#include "exec/plugin-gen.h"
+#include "exec/cpu_ldst.h"
#include "tcg/tcg-op-common.h"
#include "internal-target.h"
+#include "disas/disas.h"
static void set_can_do_io(DisasContextBase *db, bool val)
{
@@ -129,8 +131,11 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
db->max_insns = *max_insns;
db->singlestep_enabled = cflags & CF_SINGLE_STEP;
db->insn_start = NULL;
+ db->fake_insn = false;
db->host_addr[0] = host_pc;
db->host_addr[1] = NULL;
+ db->record_start = 0;
+ db->record_len = 0;
ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@@ -140,7 +145,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
ops->tb_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
- plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
+ plugin_enabled = plugin_gen_tb_start(cpu, db);
db->plugin_enabled = plugin_enabled;
while (true) {
@@ -222,159 +227,249 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
FILE *logfile = qemu_log_trylock();
if (logfile) {
fprintf(logfile, "----------------\n");
- ops->disas_log(db, cpu, logfile);
+
+ if (!ops->disas_log ||
+ !ops->disas_log(db, cpu, logfile)) {
+ fprintf(logfile, "IN: %s\n", lookup_symbol(db->pc_first));
+ target_disas(logfile, cpu, db);
+ }
fprintf(logfile, "\n");
qemu_log_unlock(logfile);
}
}
}
-static void *translator_access(CPUArchState *env, DisasContextBase *db,
- vaddr pc, size_t len)
+static bool translator_ld(CPUArchState *env, DisasContextBase *db,
+ void *dest, vaddr pc, size_t len)
{
+ TranslationBlock *tb = db->tb;
+ vaddr last = pc + len - 1;
void *host;
- vaddr base, end;
- TranslationBlock *tb;
-
- tb = db->tb;
+ vaddr base;
/* Use slow path if first page is MMIO. */
if (unlikely(tb_page_addr0(tb) == -1)) {
- return NULL;
+ /* We capped translation with first page MMIO in tb_gen_code. */
+ tcg_debug_assert(db->max_insns == 1);
+ return false;
}
- end = pc + len - 1;
- if (likely(is_same_page(db, end))) {
- host = db->host_addr[0];
- base = db->pc_first;
- } else {
- host = db->host_addr[1];
- base = TARGET_PAGE_ALIGN(db->pc_first);
- if (host == NULL) {
- tb_page_addr_t page0, old_page1, new_page1;
-
- new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]);
-
- /*
- * If the second page is MMIO, treat as if the first page
- * was MMIO as well, so that we do not cache the TB.
- */
- if (unlikely(new_page1 == -1)) {
- tb_unlock_pages(tb);
- tb_set_page_addr0(tb, -1);
- return NULL;
- }
+ host = db->host_addr[0];
+ base = db->pc_first;
- /*
- * If this is not the first time around, and page1 matches,
- * then we already have the page locked. Alternately, we're
- * not doing anything to prevent the PTE from changing, so
- * we might wind up with a different page, requiring us to
- * re-do the locking.
- */
- old_page1 = tb_page_addr1(tb);
- if (likely(new_page1 != old_page1)) {
- page0 = tb_page_addr0(tb);
- if (unlikely(old_page1 != -1)) {
- tb_unlock_page1(page0, old_page1);
- }
- tb_set_page_addr1(tb, new_page1);
- tb_lock_page1(page0, new_page1);
- }
- host = db->host_addr[1];
+ if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) {
+ /* Entire read is from the first page. */
+ memcpy(dest, host + (pc - base), len);
+ return true;
+ }
+
+ if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) {
+ /* Read begins on the first page and extends to the second. */
+ size_t len0 = -(pc | TARGET_PAGE_MASK);
+ memcpy(dest, host + (pc - base), len0);
+ pc += len0;
+ dest += len0;
+ len -= len0;
+ }
+
+ /*
+ * The read must conclude on the second page and not extend to a third.
+ *
+ * TODO: We could allow the two pages to be virtually discontiguous,
+ * since we already allow the two pages to be physically discontiguous.
+ * The only reasonable use case would be executing an insn at the end
+ * of the address space wrapping around to the beginning. For that,
+ * we would need to know the current width of the address space.
+ * In the meantime, assert.
+ */
+ base = (base & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ assert(((base ^ pc) & TARGET_PAGE_MASK) == 0);
+ assert(((base ^ last) & TARGET_PAGE_MASK) == 0);
+ host = db->host_addr[1];
+
+ if (host == NULL) {
+ tb_page_addr_t page0, old_page1, new_page1;
+
+ new_page1 = get_page_addr_code_hostp(env, base, &db->host_addr[1]);
+
+ /*
+ * If the second page is MMIO, treat as if the first page
+ * was MMIO as well, so that we do not cache the TB.
+ */
+ if (unlikely(new_page1 == -1)) {
+ tb_unlock_pages(tb);
+ tb_set_page_addr0(tb, -1);
+ /* Require that this be the final insn. */
+ db->max_insns = db->num_insns;
+ return false;
}
- /* Use slow path when crossing pages. */
- if (is_same_page(db, pc)) {
- return NULL;
+ /*
+ * If this is not the first time around, and page1 matches,
+ * then we already have the page locked. Alternately, we're
+ * not doing anything to prevent the PTE from changing, so
+ * we might wind up with a different page, requiring us to
+ * re-do the locking.
+ */
+ old_page1 = tb_page_addr1(tb);
+ if (likely(new_page1 != old_page1)) {
+ page0 = tb_page_addr0(tb);
+ if (unlikely(old_page1 != -1)) {
+ tb_unlock_page1(page0, old_page1);
+ }
+ tb_set_page_addr1(tb, new_page1);
+ tb_lock_page1(page0, new_page1);
}
+ host = db->host_addr[1];
}
- tcg_debug_assert(pc >= base);
- return host + (pc - base);
+ memcpy(dest, host + (pc - base), len);
+ return true;
}
-static void plugin_insn_append(abi_ptr pc, const void *from, size_t size)
+static void record_save(DisasContextBase *db, vaddr pc,
+ const void *from, int size)
{
-#ifdef CONFIG_PLUGIN
- struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn;
- abi_ptr off;
+ int offset;
- if (insn == NULL) {
+ /* Do not record probes before the start of TB. */
+ if (pc < db->pc_first) {
return;
}
- off = pc - insn->vaddr;
- if (off < insn->data->len) {
- g_byte_array_set_size(insn->data, off);
- } else if (off > insn->data->len) {
- /* we have an unexpected gap */
- g_assert_not_reached();
+
+ /*
+ * In translator_access, we verified that pc is within 2 pages
+ * of pc_first, thus this will never overflow.
+ */
+ offset = pc - db->pc_first;
+
+ /*
+ * Either the first or second page may be I/O. If it is the second,
+ * then the first byte we need to record will be at a non-zero offset.
+ * In either case, we should not need to record but a single insn.
+ */
+ if (db->record_len == 0) {
+ db->record_start = offset;
+ db->record_len = size;
+ } else {
+ assert(offset == db->record_start + db->record_len);
+ assert(db->record_len + size <= sizeof(db->record));
+ db->record_len += size;
+ }
+
+ memcpy(db->record + (offset - db->record_start), from, size);
+}
+
+size_t translator_st_len(const DisasContextBase *db)
+{
+ return db->fake_insn ? db->record_len : db->tb->size;
+}
+
+bool translator_st(const DisasContextBase *db, void *dest,
+ vaddr addr, size_t len)
+{
+ size_t offset, offset_end;
+
+ if (addr < db->pc_first) {
+ return false;
+ }
+ offset = addr - db->pc_first;
+ offset_end = offset + len;
+ if (offset_end > translator_st_len(db)) {
+ return false;
+ }
+
+ if (!db->fake_insn) {
+ size_t offset_page1 = -(db->pc_first | TARGET_PAGE_MASK);
+
+ /* Get all the bytes from the first page. */
+ if (db->host_addr[0]) {
+ if (offset_end <= offset_page1) {
+ memcpy(dest, db->host_addr[0] + offset, len);
+ return true;
+ }
+ if (offset < offset_page1) {
+ size_t len0 = offset_page1 - offset;
+ memcpy(dest, db->host_addr[0] + offset, len0);
+ offset += len0;
+ dest += len0;
+ }
+ }
+
+ /* Get any bytes from the second page. */
+ if (db->host_addr[1] && offset >= offset_page1) {
+ memcpy(dest, db->host_addr[1] + (offset - offset_page1),
+ offset_end - offset);
+ return true;
+ }
}
- insn->data = g_byte_array_append(insn->data, from, size);
-#endif
+ /* Else get recorded bytes. */
+ if (db->record_len != 0 &&
+ offset >= db->record_start &&
+ offset_end <= db->record_start + db->record_len) {
+ memcpy(dest, db->record + (offset - db->record_start),
+ offset_end - offset);
+ return true;
+ }
+ return false;
}
-uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
+uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
- uint8_t ret;
- void *p = translator_access(env, db, pc, sizeof(ret));
+ uint8_t raw;
- if (p) {
- plugin_insn_append(pc, p, sizeof(ret));
- return ldub_p(p);
+ if (!translator_ld(env, db, &raw, pc, sizeof(raw))) {
+ raw = cpu_ldub_code(env, pc);
+ record_save(db, pc, &raw, sizeof(raw));
}
- ret = cpu_ldub_code(env, pc);
- plugin_insn_append(pc, &ret, sizeof(ret));
- return ret;
+ return raw;
}
-uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
+uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
- uint16_t ret, plug;
- void *p = translator_access(env, db, pc, sizeof(ret));
+ uint16_t raw, tgt;
- if (p) {
- plugin_insn_append(pc, p, sizeof(ret));
- return lduw_p(p);
+ if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
+ tgt = tswap16(raw);
+ } else {
+ tgt = cpu_lduw_code(env, pc);
+ raw = tswap16(tgt);
+ record_save(db, pc, &raw, sizeof(raw));
}
- ret = cpu_lduw_code(env, pc);
- plug = tswap16(ret);
- plugin_insn_append(pc, &plug, sizeof(ret));
- return ret;
+ return tgt;
}
-uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
+uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
- uint32_t ret, plug;
- void *p = translator_access(env, db, pc, sizeof(ret));
+ uint32_t raw, tgt;
- if (p) {
- plugin_insn_append(pc, p, sizeof(ret));
- return ldl_p(p);
+ if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
+ tgt = tswap32(raw);
+ } else {
+ tgt = cpu_ldl_code(env, pc);
+ raw = tswap32(tgt);
+ record_save(db, pc, &raw, sizeof(raw));
}
- ret = cpu_ldl_code(env, pc);
- plug = tswap32(ret);
- plugin_insn_append(pc, &plug, sizeof(ret));
- return ret;
+ return tgt;
}
-uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
+uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc)
{
- uint64_t ret, plug;
- void *p = translator_access(env, db, pc, sizeof(ret));
+ uint64_t raw, tgt;
- if (p) {
- plugin_insn_append(pc, p, sizeof(ret));
- return ldq_p(p);
+ if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
+ tgt = tswap64(raw);
+ } else {
+ tgt = cpu_ldq_code(env, pc);
+ raw = tswap64(tgt);
+ record_save(db, pc, &raw, sizeof(raw));
}
- ret = cpu_ldq_code(env, pc);
- plug = tswap64(ret);
- plugin_insn_append(pc, &plug, sizeof(ret));
- return ret;
+ return tgt;
}
-void translator_fake_ldb(uint8_t insn8, abi_ptr pc)
+void translator_fake_ld(DisasContextBase *db, const void *data, size_t len)
{
- plugin_insn_append(pc, &insn8, sizeof(insn8));
+ db->fake_insn = true;
+ record_save(db, db->pc_first, data, len);
}
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c
index 1c621477ad..80d24540ed 100644
--- a/accel/tcg/user-exec.c
+++ b/accel/tcg/user-exec.c
@@ -24,7 +24,9 @@
#include "qemu/bitops.h"
#include "qemu/rcu.h"
#include "exec/cpu_ldst.h"
+#include "qemu/main-loop.h"
#include "exec/translate-all.h"
+#include "exec/page-protection.h"
#include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#include "trace/trace-root.h"
@@ -37,6 +39,13 @@ __thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
+void cpu_interrupt(CPUState *cpu, int mask)
+{
+ g_assert(bql_locked());
+ cpu->interrupt_request |= mask;
+ qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
+}
+
/*
* Adjust the pc to pass to cpu_restore_state; return the memop type.
*/
@@ -765,7 +774,7 @@ int page_unprotect(target_ulong address, uintptr_t pc)
if (prot & PAGE_EXEC) {
prot = (prot & ~PAGE_EXEC) | PAGE_READ;
}
- mprotect((void *)g2h_untagged(start), len, prot & PAGE_BITS);
+ mprotect((void *)g2h_untagged(start), len, prot & PAGE_RWX);
}
mmap_unlock();
diff --git a/accel/tcg/vcpu-state.h b/accel/tcg/vcpu-state.h
new file mode 100644
index 0000000000..e407d914df
--- /dev/null
+++ b/accel/tcg/vcpu-state.h
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org>
+ * SPDX-FileCopyrightText: 2023 Linaro Ltd.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef ACCEL_TCG_VCPU_STATE_H
+#define ACCEL_TCG_VCPU_STATE_H
+
+#include "hw/core/cpu.h"
+
+#ifdef CONFIG_USER_ONLY
+static inline TaskState *get_task_state(const CPUState *cs)
+{
+ return cs->opaque;
+}
+#endif
+
+#endif
diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index a514bbb310..940104ee55 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "sysemu/cryptodev.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "crypto/cipher.h"
@@ -396,8 +397,8 @@ static int cryptodev_builtin_create_session(
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
default:
- error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
- sess_info->op_code);
+ error_report("Unsupported opcode :%" PRIu32 "",
+ sess_info->op_code);
return -VIRTIO_CRYPTO_NOTSUPP;
}
@@ -554,8 +555,8 @@ static int cryptodev_builtin_operation(
if (op_info->session_id >= MAX_NUM_SESSIONS ||
builtin->sessions[op_info->session_id] == NULL) {
- error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
- op_info->session_id);
+ error_report("Cannot find a valid session id: %" PRIu64 "",
+ op_info->session_id);
return -VIRTIO_CRYPTO_INVSESS;
}
diff --git a/backends/iommufd.c b/backends/iommufd.c
index 76a0204852..c506afbdac 100644
--- a/backends/iommufd.c
+++ b/backends/iommufd.c
@@ -72,24 +72,22 @@ static void iommufd_backend_class_init(ObjectClass *oc, void *data)
object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
}
-int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
+bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
{
- int fd, ret = 0;
+ int fd;
if (be->owned && !be->users) {
fd = qemu_open_old("/dev/iommu", O_RDWR);
if (fd < 0) {
error_setg_errno(errp, errno, "/dev/iommu opening failed");
- ret = fd;
- goto out;
+ return false;
}
be->fd = fd;
}
be->users++;
-out:
- trace_iommufd_backend_connect(be->fd, be->owned,
- be->users, ret);
- return ret;
+
+ trace_iommufd_backend_connect(be->fd, be->owned, be->users);
+ return true;
}
void iommufd_backend_disconnect(IOMMUFDBackend *be)
@@ -106,25 +104,24 @@ out:
trace_iommufd_backend_disconnect(be->fd, be->users);
}
-int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
- Error **errp)
+bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
+ Error **errp)
{
- int ret, fd = be->fd;
+ int fd = be->fd;
struct iommu_ioas_alloc alloc_data = {
.size = sizeof(alloc_data),
.flags = 0,
};
- ret = ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data);
- if (ret) {
+ if (ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data)) {
error_setg_errno(errp, errno, "Failed to allocate ioas");
- return ret;
+ return false;
}
*ioas_id = alloc_data.out_ioas_id;
- trace_iommufd_backend_alloc_ioas(fd, *ioas_id, ret);
+ trace_iommufd_backend_alloc_ioas(fd, *ioas_id);
- return ret;
+ return true;
}
void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
diff --git a/backends/trace-events b/backends/trace-events
index d45c6e31a6..211e6f374a 100644
--- a/backends/trace-events
+++ b/backends/trace-events
@@ -7,11 +7,11 @@ dbus_vmstate_loading(const char *id) "id: %s"
dbus_vmstate_saving(const char *id) "id: %s"
# iommufd.c
-iommufd_backend_connect(int fd, bool owned, uint32_t users, int ret) "fd=%d owned=%d users=%d (%d)"
+iommufd_backend_connect(int fd, bool owned, uint32_t users) "fd=%d owned=%d users=%d"
iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d"
iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d"
iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)"
iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
-iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas, int ret) " iommufd=%d ioas=%d (%d)"
+iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d"
iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)"
diff --git a/block/gluster.c b/block/gluster.c
index 4253c8db5e..d0999903df 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -17,7 +17,6 @@
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
-#include "qemu/uri.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -289,9 +288,9 @@ static void glfs_clear_preopened(glfs_t *fs)
}
}
-static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
+static int parse_volume_options(BlockdevOptionsGluster *gconf, const char *path)
{
- char *p, *q;
+ const char *p, *q;
if (!path) {
return -EINVAL;
@@ -349,13 +348,13 @@ static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path)
static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
const char *filename)
{
+ g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
+ g_autoptr(GHashTable) qp = NULL;
SocketAddress *gsconf;
- URI *uri;
- QueryParams *qp = NULL;
bool is_unix = false;
- int ret = 0;
+ const char *uri_scheme, *uri_query, *uri_server;
+ int uri_port, ret;
- uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
}
@@ -364,54 +363,54 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf,
QAPI_LIST_PREPEND(gconf->server, gsconf);
/* transport */
- if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
+ uri_scheme = g_uri_get_scheme(uri);
+ if (!uri_scheme || !strcmp(uri_scheme, "gluster")) {
gsconf->type = SOCKET_ADDRESS_TYPE_INET;
- } else if (!strcmp(uri->scheme, "gluster+tcp")) {
+ } else if (!strcmp(uri_scheme, "gluster+tcp")) {
gsconf->type = SOCKET_ADDRESS_TYPE_INET;
- } else if (!strcmp(uri->scheme, "gluster+unix")) {
+ } else if (!strcmp(uri_scheme, "gluster+unix")) {
gsconf->type = SOCKET_ADDRESS_TYPE_UNIX;
is_unix = true;
} else {
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
- ret = parse_volume_options(gconf, uri->path);
+ ret = parse_volume_options(gconf, g_uri_get_path(uri));
if (ret < 0) {
- goto out;
+ return ret;
}
- qp = query_params_parse(uri->query);
- if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
- ret = -EINVAL;
- goto out;
+ uri_query = g_uri_get_query(uri);
+ if (uri_query) {
+ qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL);
+ if (!qp) {
+ return -EINVAL;
+ }
+ ret = g_hash_table_size(qp);
+ if (ret > 1 || (is_unix && !ret) || (!is_unix && ret)) {
+ return -EINVAL;
+ }
}
+ uri_server = g_uri_get_host(uri);
+ uri_port = g_uri_get_port(uri);
+
if (is_unix) {
- if (uri->server || uri->port) {
- ret = -EINVAL;
- goto out;
- }
- if (strcmp(qp->p[0].name, "socket")) {
- ret = -EINVAL;
- goto out;
+ char *uri_socket = g_hash_table_lookup(qp, "socket");
+ if (uri_server || uri_port != -1 || !uri_socket) {
+ return -EINVAL;
}
- gsconf->u.q_unix.path = g_strdup(qp->p[0].value);
+ gsconf->u.q_unix.path = g_strdup(uri_socket);
} else {
- gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost");
- if (uri->port) {
- gsconf->u.inet.port = g_strdup_printf("%d", uri->port);
+ gsconf->u.inet.host = g_strdup(uri_server ? uri_server : "localhost");
+ if (uri_port > 0) {
+ gsconf->u.inet.port = g_strdup_printf("%d", uri_port);
} else {
gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT);
}
}
-out:
- if (qp) {
- query_params_free(qp);
- }
- uri_free(uri);
- return ret;
+ return 0;
}
static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
diff --git a/block/nbd.c b/block/nbd.c
index ef05f7cdfd..589d28af83 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -31,7 +31,6 @@
#include "qemu/osdep.h"
#include "trace.h"
-#include "qemu/uri.h"
#include "qemu/option.h"
#include "qemu/cutils.h"
#include "qemu/main-loop.h"
@@ -1514,30 +1513,31 @@ static void nbd_client_close(BlockDriverState *bs)
static int nbd_parse_uri(const char *filename, QDict *options)
{
- URI *uri;
+ g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
+ g_autoptr(GHashTable) qp = NULL;
const char *p;
- QueryParams *qp = NULL;
- int ret = 0;
+ int qp_n;
bool is_unix;
+ const char *uri_scheme, *uri_query, *uri_server;
+ int uri_port;
- uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
}
/* transport */
- if (!g_strcmp0(uri->scheme, "nbd")) {
+ uri_scheme = g_uri_get_scheme(uri);
+ if (!g_strcmp0(uri_scheme, "nbd")) {
is_unix = false;
- } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) {
+ } else if (!g_strcmp0(uri_scheme, "nbd+tcp")) {
is_unix = false;
- } else if (!g_strcmp0(uri->scheme, "nbd+unix")) {
+ } else if (!g_strcmp0(uri_scheme, "nbd+unix")) {
is_unix = true;
} else {
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
- p = uri->path ? uri->path : "";
+ p = g_uri_get_path(uri) ?: "";
if (p[0] == '/') {
p++;
}
@@ -1545,52 +1545,50 @@ static int nbd_parse_uri(const char *filename, QDict *options)
qdict_put_str(options, "export", p);
}
- qp = query_params_parse(uri->query);
- if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
- ret = -EINVAL;
- goto out;
+ uri_query = g_uri_get_query(uri);
+ if (uri_query) {
+ qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL);
+ if (!qp) {
+ return -EINVAL;
+ }
+ qp_n = g_hash_table_size(qp);
+ if (qp_n > 1 || (is_unix && !qp_n) || (!is_unix && qp_n)) {
+ return -EINVAL;
+ }
+ }
+
+ uri_server = g_uri_get_host(uri);
+ if (uri_server && !uri_server[0]) {
+ uri_server = NULL;
}
+ uri_port = g_uri_get_port(uri);
if (is_unix) {
/* nbd+unix:///export?socket=path */
- if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
- ret = -EINVAL;
- goto out;
+ const char *uri_socket = g_hash_table_lookup(qp, "socket");
+ if (uri_server || uri_port != -1 || !uri_socket) {
+ return -EINVAL;
}
qdict_put_str(options, "server.type", "unix");
- qdict_put_str(options, "server.path", qp->p[0].value);
+ qdict_put_str(options, "server.path", uri_socket);
} else {
- QString *host;
char *port_str;
/* nbd[+tcp]://host[:port]/export */
- if (!uri->server) {
- ret = -EINVAL;
- goto out;
- }
-
- /* strip braces from literal IPv6 address */
- if (uri->server[0] == '[') {
- host = qstring_from_substr(uri->server, 1,
- strlen(uri->server) - 1);
- } else {
- host = qstring_from_str(uri->server);
+ if (!uri_server) {
+ return -EINVAL;
}
qdict_put_str(options, "server.type", "inet");
- qdict_put(options, "server.host", host);
+ qdict_put_str(options, "server.host", uri_server);
- port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT);
+ port_str = g_strdup_printf("%d", uri_port > 0 ? uri_port
+ : NBD_DEFAULT_PORT);
qdict_put_str(options, "server.port", port_str);
g_free(port_str);
}
-out:
- if (qp) {
- query_params_free(qp);
- }
- uri_free(uri);
- return ret;
+ return 0;
}
static bool nbd_has_filename_options_conflict(QDict *options, Error **errp)
diff --git a/block/nfs.c b/block/nfs.c
index f737e19cd3..60240a8733 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -38,7 +38,6 @@
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/option.h"
-#include "qemu/uri.h"
#include "qemu/cutils.h"
#include "sysemu/replay.h"
#include "qapi/qapi-visit-block-core.h"
@@ -79,77 +78,76 @@ typedef struct NFSRPC {
static int nfs_parse_uri(const char *filename, QDict *options, Error **errp)
{
- URI *uri = NULL;
- QueryParams *qp = NULL;
- int ret = -EINVAL, i;
+ g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
+ GUriParamsIter qp;
+ const char *uri_server, *uri_path, *uri_query;
+ char *qp_name, *qp_value;
+ GError *gerror = NULL;
- uri = uri_parse(filename);
if (!uri) {
error_setg(errp, "Invalid URI specified");
- goto out;
+ return -EINVAL;
}
- if (g_strcmp0(uri->scheme, "nfs") != 0) {
+ if (!g_str_equal(g_uri_get_scheme(uri), "nfs")) {
error_setg(errp, "URI scheme must be 'nfs'");
- goto out;
+ return -EINVAL;
}
- if (!uri->server) {
+ uri_server = g_uri_get_host(uri);
+ if (!uri_server || !uri_server[0]) {
error_setg(errp, "missing hostname in URI");
- goto out;
+ return -EINVAL;
}
- if (!uri->path) {
+ uri_path = g_uri_get_path(uri);
+ if (!uri_path || !uri_path[0]) {
error_setg(errp, "missing file path in URI");
- goto out;
- }
-
- qp = query_params_parse(uri->query);
- if (!qp) {
- error_setg(errp, "could not parse query parameters");
- goto out;
+ return -EINVAL;
}
- qdict_put_str(options, "server.host", uri->server);
+ qdict_put_str(options, "server.host", uri_server);
qdict_put_str(options, "server.type", "inet");
- qdict_put_str(options, "path", uri->path);
-
- for (i = 0; i < qp->n; i++) {
- uint64_t val;
- if (!qp->p[i].value) {
- error_setg(errp, "Value for NFS parameter expected: %s",
- qp->p[i].name);
- goto out;
- }
- if (parse_uint_full(qp->p[i].value, 0, &val)) {
- error_setg(errp, "Illegal value for NFS parameter: %s",
- qp->p[i].name);
- goto out;
- }
- if (!strcmp(qp->p[i].name, "uid")) {
- qdict_put_str(options, "user", qp->p[i].value);
- } else if (!strcmp(qp->p[i].name, "gid")) {
- qdict_put_str(options, "group", qp->p[i].value);
- } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) {
- qdict_put_str(options, "tcp-syn-count", qp->p[i].value);
- } else if (!strcmp(qp->p[i].name, "readahead")) {
- qdict_put_str(options, "readahead-size", qp->p[i].value);
- } else if (!strcmp(qp->p[i].name, "pagecache")) {
- qdict_put_str(options, "page-cache-size", qp->p[i].value);
- } else if (!strcmp(qp->p[i].name, "debug")) {
- qdict_put_str(options, "debug", qp->p[i].value);
- } else {
- error_setg(errp, "Unknown NFS parameter name: %s",
- qp->p[i].name);
- goto out;
+ qdict_put_str(options, "path", uri_path);
+
+ uri_query = g_uri_get_query(uri);
+ if (uri_query) {
+ g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE);
+ while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) {
+ uint64_t val;
+ if (!qp_name || gerror) {
+ error_setg(errp, "Failed to parse NFS parameter");
+ return -EINVAL;
+ }
+ if (!qp_value) {
+ error_setg(errp, "Value for NFS parameter expected: %s",
+ qp_name);
+ return -EINVAL;
+ }
+ if (parse_uint_full(qp_value, 0, &val)) {
+ error_setg(errp, "Invalid value for NFS parameter: %s",
+ qp_name);
+ return -EINVAL;
+ }
+ if (g_str_equal(qp_name, "uid")) {
+ qdict_put_str(options, "user", qp_value);
+ } else if (g_str_equal(qp_name, "gid")) {
+ qdict_put_str(options, "group", qp_value);
+ } else if (g_str_equal(qp_name, "tcp-syncnt")) {
+ qdict_put_str(options, "tcp-syn-count", qp_value);
+ } else if (g_str_equal(qp_name, "readahead")) {
+ qdict_put_str(options, "readahead-size", qp_value);
+ } else if (g_str_equal(qp_name, "pagecache")) {
+ qdict_put_str(options, "page-cache-size", qp_value);
+ } else if (g_str_equal(qp_name, "debug")) {
+ qdict_put_str(options, "debug", qp_value);
+ } else {
+ error_setg(errp, "Unknown NFS parameter name: %s", qp_name);
+ return -EINVAL;
+ }
}
}
- ret = 0;
-out:
- if (qp) {
- query_params_free(qp);
- }
- uri_free(uri);
- return ret;
+
+ return 0;
}
static bool nfs_has_filename_options_conflict(QDict *options, Error **errp)
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 874ea56948..256ec99878 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -1609,7 +1609,7 @@ bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
name);
goto fail;
}
- tb = g_memdup(&bm->table, sizeof(bm->table));
+ tb = g_memdup2(&bm->table, sizeof(bm->table));
bm->table.offset = 0;
bm->table.size = 0;
QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry);
diff --git a/block/ssh.c b/block/ssh.c
index 2748253d4a..a88171d4b5 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -37,7 +37,6 @@
#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/sockets.h"
-#include "qemu/uri.h"
#include "qapi/qapi-visit-sockets.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
@@ -181,65 +180,71 @@ static void sftp_error_trace(BDRVSSHState *s, const char *op)
static int parse_uri(const char *filename, QDict *options, Error **errp)
{
- URI *uri = NULL;
- QueryParams *qp;
+ g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL);
+ const char *uri_host, *uri_path, *uri_user, *uri_query;
char *port_str;
- int i;
+ int port;
+ g_autoptr(GError) gerror = NULL;
+ char *qp_name, *qp_value;
+ GUriParamsIter qp;
- uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
}
- if (g_strcmp0(uri->scheme, "ssh") != 0) {
+ if (g_strcmp0(g_uri_get_scheme(uri), "ssh") != 0) {
error_setg(errp, "URI scheme must be 'ssh'");
- goto err;
+ return -EINVAL;
}
- if (!uri->server || strcmp(uri->server, "") == 0) {
+ uri_host = g_uri_get_host(uri);
+ if (!uri_host || g_str_equal(uri_host, "")) {
error_setg(errp, "missing hostname in URI");
- goto err;
+ return -EINVAL;
}
- if (!uri->path || strcmp(uri->path, "") == 0) {
+ uri_path = g_uri_get_path(uri);
+ if (!uri_path || g_str_equal(uri_path, "")) {
error_setg(errp, "missing remote path in URI");
- goto err;
- }
-
- qp = query_params_parse(uri->query);
- if (!qp) {
- error_setg(errp, "could not parse query parameters");
- goto err;
+ return -EINVAL;
}
- if(uri->user && strcmp(uri->user, "") != 0) {
- qdict_put_str(options, "user", uri->user);
+ uri_user = g_uri_get_user(uri);
+ if (uri_user && !g_str_equal(uri_user, "")) {
+ qdict_put_str(options, "user", uri_user);
}
- qdict_put_str(options, "server.host", uri->server);
+ qdict_put_str(options, "server.host", uri_host);
- port_str = g_strdup_printf("%d", uri->port ?: 22);
+ port = g_uri_get_port(uri);
+ port_str = g_strdup_printf("%d", port > 0 ? port : 22);
qdict_put_str(options, "server.port", port_str);
g_free(port_str);
- qdict_put_str(options, "path", uri->path);
-
- /* Pick out any query parameters that we understand, and ignore
- * the rest.
- */
- for (i = 0; i < qp->n; ++i) {
- if (strcmp(qp->p[i].name, "host_key_check") == 0) {
- qdict_put_str(options, "host_key_check", qp->p[i].value);
+ qdict_put_str(options, "path", uri_path);
+
+ uri_query = g_uri_get_query(uri);
+ if (uri_query) {
+ g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE);
+ while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) {
+ if (!qp_name || !qp_value || gerror) {
+ warn_report("Failed to parse SSH URI parameters '%s'",
+ uri_query);
+ break;
+ }
+ /*
+ * Pick out the query parameters that we understand, and ignore
+ * (or rather warn about) the rest.
+ */
+ if (g_str_equal(qp_name, "host_key_check")) {
+ qdict_put_str(options, "host_key_check", qp_value);
+ } else {
+ warn_report("Unsupported parameter '%s' in URI", qp_name);
+ }
}
}
- query_params_free(qp);
- uri_free(uri);
return 0;
-
- err:
- uri_free(uri);
- return -EINVAL;
}
static bool ssh_has_filename_options_conflict(QDict *options, Error **errp)
diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h
index 21d9bab889..eef6b222d9 100644
--- a/bsd-user/bsd-mem.h
+++ b/bsd-user/bsd-mem.h
@@ -56,6 +56,7 @@
#include <fcntl.h>
#include "qemu-bsd.h"
+#include "exec/page-protection.h"
extern struct bsd_shm_regions bsd_shm_regions[];
extern abi_ulong target_brk;
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
index 3ef11b2807..f3a4f1712d 100644
--- a/bsd-user/mmap.c
+++ b/bsd-user/mmap.c
@@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+#include "exec/page-protection.h"
#include "qemu.h"
@@ -96,7 +97,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
end = host_end;
}
ret = mprotect(g2h_untagged(host_start),
- qemu_host_page_size, prot1 & PAGE_BITS);
+ qemu_host_page_size, prot1 & PAGE_RWX);
if (ret != 0)
goto error;
host_start += qemu_host_page_size;
@@ -107,7 +108,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
prot1 |= page_get_flags(addr);
}
ret = mprotect(g2h_untagged(host_end - qemu_host_page_size),
- qemu_host_page_size, prot1 & PAGE_BITS);
+ qemu_host_page_size, prot1 & PAGE_RWX);
if (ret != 0)
goto error;
host_end -= qemu_host_page_size;
@@ -174,7 +175,7 @@ static int mmap_frag(abi_ulong real_start,
return -1;
prot1 = prot;
}
- prot1 &= PAGE_BITS;
+ prot1 &= PAGE_RWX;
prot_new = prot | prot1;
if (fd != -1) {
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
index 8629f0dcde..9d2fc7148e 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -22,11 +22,11 @@
#include "exec/cpu_ldst.h"
#include "exec/exec-all.h"
-#include "exec/user/abitypes.h"
+#include "user/abitypes.h"
extern char **environ;
-#include "exec/user/thunk.h"
+#include "user/thunk.h"
#include "target_arch.h"
#include "syscall_defs.h"
#include "target_syscall.h"
@@ -34,7 +34,9 @@ extern char **environ;
#include "target_os_signal.h"
#include "target.h"
#include "exec/gdbstub.h"
+#include "exec/page-protection.h"
#include "qemu/clang-tsa.h"
+#include "accel/tcg/vcpu-state.h"
#include "qemu-os.h"
/*
@@ -75,7 +77,7 @@ struct emulated_sigtable {
/*
* NOTE: we force a big alignment so that the stack stored after is aligned too
*/
-typedef struct TaskState {
+struct TaskState {
pid_t ts_tid; /* tid (or pid) of this task */
struct TaskState *next;
@@ -113,12 +115,7 @@ typedef struct TaskState {
/* This thread's sigaltstack, if it has one */
struct target_sigaltstack sigaltstack_used;
-} __attribute__((aligned(16))) TaskState;
-
-static inline TaskState *get_task_state(CPUState *cs)
-{
- return cs->opaque;
-}
+} __attribute__((aligned(16)));
void stop_all_tasks(void);
extern const char *interp_prefix;
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
index b2faf1d0dd..8b6654b91d 100644
--- a/bsd-user/signal.c
+++ b/bsd-user/signal.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu.h"
+#include "exec/page-protection.h"
#include "user/tswap-target.h"
#include "gdbstub/user.h"
#include "signal-common.h"
diff --git a/configs/devices/alpha-softmmu/default.mak b/configs/devices/alpha-softmmu/default.mak
index d186fe8e9b..3de6a9f577 100644
--- a/configs/devices/alpha-softmmu/default.mak
+++ b/configs/devices/alpha-softmmu/default.mak
@@ -5,6 +5,5 @@
#CONFIG_PCI_DEVICES=n
#CONFIG_TEST_DEVICES=n
-# Boards:
-#
-CONFIG_DP264=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_DP264=n
diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak
index 6ee31bc1ab..31f77c2026 100644
--- a/configs/devices/arm-softmmu/default.mak
+++ b/configs/devices/arm-softmmu/default.mak
@@ -1,9 +1,12 @@
# Default configuration for arm-softmmu
+# Uncomment the following lines to disable these optional devices:
+# CONFIG_I2C_DEVICES=n
# CONFIG_PCI_DEVICES=n
# CONFIG_TEST_DEVICES=n
-CONFIG_ARM_VIRT=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_ARM_VIRT=n
# These are selected by default when TCG is enabled, uncomment them to
# keep out of the build.
diff --git a/configs/devices/avr-softmmu/default.mak b/configs/devices/avr-softmmu/default.mak
index 80218add98..4207e7b3ce 100644
--- a/configs/devices/avr-softmmu/default.mak
+++ b/configs/devices/avr-softmmu/default.mak
@@ -1,5 +1,4 @@
# Default configuration for avr-softmmu
-# Boards:
-#
-CONFIG_ARDUINO=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_ARDUINO=n
diff --git a/configs/devices/cris-softmmu/default.mak b/configs/devices/cris-softmmu/default.mak
index 5932cf4d06..ff73cd4084 100644
--- a/configs/devices/cris-softmmu/default.mak
+++ b/configs/devices/cris-softmmu/default.mak
@@ -1,5 +1,4 @@
# Default configuration for cris-softmmu
-# Boards:
-#
-CONFIG_AXIS=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_AXIS=n
diff --git a/configs/devices/hppa-softmmu/default.mak b/configs/devices/hppa-softmmu/default.mak
index b0364bb88f..059510cdbb 100644
--- a/configs/devices/hppa-softmmu/default.mak
+++ b/configs/devices/hppa-softmmu/default.mak
@@ -4,6 +4,5 @@
#
#CONFIG_PCI_DEVICES=n
-# Boards:
-#
-CONFIG_HPPA_B160L=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_HPPA_B160L=n
diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak
index 598c6646df..448e3e3b1b 100644
--- a/configs/devices/i386-softmmu/default.mak
+++ b/configs/devices/i386-softmmu/default.mak
@@ -24,9 +24,8 @@
#CONFIG_VTD=n
#CONFIG_SGX=n
-# Boards:
-#
-CONFIG_ISAPC=y
-CONFIG_I440FX=y
-CONFIG_Q35=y
-CONFIG_MICROVM=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_ISAPC=n
+# CONFIG_I440FX=n
+# CONFIG_Q35=n
+# CONFIG_MICROVM=n
diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak
index 928bc117ef..ffe705836f 100644
--- a/configs/devices/loongarch64-softmmu/default.mak
+++ b/configs/devices/loongarch64-softmmu/default.mak
@@ -1,3 +1,7 @@
# Default configuration for loongarch64-softmmu
-CONFIG_LOONGARCH_VIRT=y
+# Uncomment the following lines to disable these optional devices:
+# CONFIG_PCI_DEVICES=n
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_LOONGARCH_VIRT=n
diff --git a/configs/devices/m68k-softmmu/default.mak b/configs/devices/m68k-softmmu/default.mak
index 8dcaa28ed3..3ceda6b041 100644
--- a/configs/devices/m68k-softmmu/default.mak
+++ b/configs/devices/m68k-softmmu/default.mak
@@ -1,9 +1,8 @@
# Default configuration for m68k-softmmu
-# Boards:
-#
-CONFIG_AN5206=y
-CONFIG_MCF5208=y
-CONFIG_NEXTCUBE=y
-CONFIG_Q800=y
-CONFIG_M68K_VIRT=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_AN5206=n
+# CONFIG_MCF5208=n
+# CONFIG_NEXTCUBE=n
+# CONFIG_Q800=n
+# CONFIG_M68K_VIRT=n
diff --git a/configs/devices/microblaze-softmmu/default.mak b/configs/devices/microblaze-softmmu/default.mak
index db8c6e4bba..583e3959bb 100644
--- a/configs/devices/microblaze-softmmu/default.mak
+++ b/configs/devices/microblaze-softmmu/default.mak
@@ -1,7 +1,6 @@
# Default configuration for microblaze-softmmu
-# Boards:
-#
-CONFIG_PETALOGIX_S3ADSP1800=y
-CONFIG_PETALOGIX_ML605=y
-CONFIG_XLNX_ZYNQMP_PMU=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_PETALOGIX_S3ADSP1800=n
+# CONFIG_PETALOGIX_ML605=n
+# CONFIG_XLNX_ZYNQMP_PMU=n
diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak
index 416a5d353e..b50107feaf 100644
--- a/configs/devices/mips-softmmu/common.mak
+++ b/configs/devices/mips-softmmu/common.mak
@@ -4,5 +4,6 @@
# CONFIG_PCI_DEVICES=n
# CONFIG_TEST_DEVICES=n
-CONFIG_MALTA=y
-CONFIG_MIPSSIM=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_MALTA=n
+# CONFIG_MIPSSIM=n
diff --git a/configs/devices/mips64-softmmu/default.mak b/configs/devices/mips64-softmmu/default.mak
index 566672f3c2..1b8d7ced1c 100644
--- a/configs/devices/mips64-softmmu/default.mak
+++ b/configs/devices/mips64-softmmu/default.mak
@@ -1,4 +1,6 @@
# Default configuration for mips64-softmmu
include ../mips-softmmu/common.mak
-CONFIG_JAZZ=y
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_JAZZ=n
diff --git a/configs/devices/mips64el-softmmu/default.mak b/configs/devices/mips64el-softmmu/default.mak
index 88a37cf27f..9dce346c4f 100644
--- a/configs/devices/mips64el-softmmu/default.mak
+++ b/configs/devices/mips64el-softmmu/default.mak
@@ -1,7 +1,9 @@
# Default configuration for mips64el-softmmu
include ../mips-softmmu/common.mak
-CONFIG_FULOONG=y
-CONFIG_LOONGSON3V=y
-CONFIG_JAZZ=y
-CONFIG_MIPS_BOSTON=y
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_FULOONG=n
+# CONFIG_LOONGSON3V=n
+# CONFIG_JAZZ=n
+# CONFIG_MIPS_BOSTON=n
diff --git a/configs/devices/or1k-softmmu/default.mak b/configs/devices/or1k-softmmu/default.mak
index 89c39e3123..efe3bc278b 100644
--- a/configs/devices/or1k-softmmu/default.mak
+++ b/configs/devices/or1k-softmmu/default.mak
@@ -1,6 +1,9 @@
# Default configuration for or1k-softmmu
+# Uncomment the following lines to disable these optional devices:
+# CONFIG_PCI_DEVICES=n
+# CONFIG_TEST_DEVICES=n
+
# Boards:
-#
-CONFIG_OR1K_SIM=y
-CONFIG_OR1K_VIRT=y
+# CONFIG_OR1K_SIM=n
+# CONFIG_OR1K_VIRT=n
diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak
index b85fd2bcd7..460d15e676 100644
--- a/configs/devices/ppc-softmmu/default.mak
+++ b/configs/devices/ppc-softmmu/default.mak
@@ -1,21 +1,27 @@
# Default configuration for ppc-softmmu
-# For embedded PPCs:
-CONFIG_E500PLAT=y
-CONFIG_MPC8544DS=y
-CONFIG_PPC405=y
-CONFIG_PPC440=y
-CONFIG_VIRTEX=y
+# Uncomment the following lines to disable these optional devices:
+# CONFIG_PCI_DEVICES=n
+# CONFIG_TEST_DEVICES=n
+
+# Boards are selected by default, uncomment to keep out of the build.
+
+# Embedded PPCs:
+# CONFIG_E500PLAT=n
+# CONFIG_MPC8544DS=n
+# CONFIG_PPC405=n
+# CONFIG_PPC440=n
+# CONFIG_VIRTEX=n
# For Sam460ex
-CONFIG_SAM460EX=y
+# CONFIG_SAM460EX=n
# For Macs
-CONFIG_MAC_OLDWORLD=y
-CONFIG_MAC_NEWWORLD=y
+# CONFIG_MAC_OLDWORLD=n
+# CONFIG_MAC_NEWWORLD=n
-CONFIG_AMIGAONE=y
-CONFIG_PEGASOS2=y
+# CONFIG_AMIGAONE=n
+# CONFIG_PEGASOS2=n
# For PReP
-CONFIG_PREP=y
+# CONFIG_PREP=n
diff --git a/configs/devices/ppc64-softmmu/default.mak b/configs/devices/ppc64-softmmu/default.mak
index b90e5bf455..e8ad260313 100644
--- a/configs/devices/ppc64-softmmu/default.mak
+++ b/configs/devices/ppc64-softmmu/default.mak
@@ -3,8 +3,6 @@
# Include all 32-bit boards
include ../ppc-softmmu/default.mak
-# For PowerNV
-CONFIG_POWERNV=y
-
-# For pSeries
-CONFIG_PSERIES=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_POWERNV=n
+# CONFIG_PSERIES=n
diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak
index 94a236c9c2..c2cd86ce05 100644
--- a/configs/devices/riscv32-softmmu/default.mak
+++ b/configs/devices/riscv32-softmmu/default.mak
@@ -1,13 +1,12 @@
# Default configuration for riscv32-softmmu
# Uncomment the following lines to disable these optional devices:
-#
-#CONFIG_PCI_DEVICES=n
+# CONFIG_PCI_DEVICES=n
+# CONFIG_TEST_DEVICES=n
-# Boards:
-#
-CONFIG_SPIKE=y
-CONFIG_SIFIVE_E=y
-CONFIG_SIFIVE_U=y
-CONFIG_RISCV_VIRT=y
-CONFIG_OPENTITAN=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_SPIKE=n
+# CONFIG_SIFIVE_E=n
+# CONFIG_SIFIVE_U=n
+# CONFIG_RISCV_VIRT=n
+# CONFIG_OPENTITAN=n
diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak
index 3f68059448..39ed3a0061 100644
--- a/configs/devices/riscv64-softmmu/default.mak
+++ b/configs/devices/riscv64-softmmu/default.mak
@@ -1,14 +1,13 @@
# Default configuration for riscv64-softmmu
# Uncomment the following lines to disable these optional devices:
-#
-#CONFIG_PCI_DEVICES=n
+# CONFIG_PCI_DEVICES=n
+# CONFIG_TEST_DEVICES=n
-# Boards:
-#
-CONFIG_SPIKE=y
-CONFIG_SIFIVE_E=y
-CONFIG_SIFIVE_U=y
-CONFIG_RISCV_VIRT=y
-CONFIG_MICROCHIP_PFSOC=y
-CONFIG_SHAKTI_C=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_SPIKE=n
+# CONFIG_SIFIVE_E=n
+# CONFIG_SIFIVE_U=n
+# CONFIG_RISCV_VIRT=n
+# CONFIG_MICROCHIP_PFSOC=n
+# CONFIG_SHAKTI_C=n
diff --git a/configs/devices/rx-softmmu/default.mak b/configs/devices/rx-softmmu/default.mak
index df2b4e4f42..e7caebe197 100644
--- a/configs/devices/rx-softmmu/default.mak
+++ b/configs/devices/rx-softmmu/default.mak
@@ -1,3 +1,4 @@
# Default configuration for rx-softmmu
-CONFIG_RX_GDBSIM=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_RX_GDBSIM=n
diff --git a/configs/devices/s390x-softmmu/default.mak b/configs/devices/s390x-softmmu/default.mak
index 6d87bc8b4b..340c109292 100644
--- a/configs/devices/s390x-softmmu/default.mak
+++ b/configs/devices/s390x-softmmu/default.mak
@@ -9,6 +9,5 @@
#CONFIG_WDT_DIAG288=n
#CONFIG_PCIE_DEVICES=n
-# Boards:
-#
-CONFIG_S390_CCW_VIRTIO=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_S390_CCW_VIRTIO=n
diff --git a/configs/devices/sh4-softmmu/default.mak b/configs/devices/sh4-softmmu/default.mak
index 565e8b0b5d..c06a427053 100644
--- a/configs/devices/sh4-softmmu/default.mak
+++ b/configs/devices/sh4-softmmu/default.mak
@@ -5,7 +5,6 @@
#CONFIG_PCI_DEVICES=n
#CONFIG_TEST_DEVICES=n
-# Boards:
-#
-CONFIG_R2D=y
-CONFIG_SHIX=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_R2D=n
+# CONFIG_SHIX=n
diff --git a/configs/devices/sparc-softmmu/default.mak b/configs/devices/sparc-softmmu/default.mak
index ee85218115..87668fda5e 100644
--- a/configs/devices/sparc-softmmu/default.mak
+++ b/configs/devices/sparc-softmmu/default.mak
@@ -5,7 +5,6 @@
#CONFIG_TCX=n
#CONFIG_CG3=n
-# Boards:
-#
-CONFIG_SUN4M=y
-CONFIG_LEON3=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_SUN4M=n
+# CONFIG_LEON3=n
diff --git a/configs/devices/sparc64-softmmu/default.mak b/configs/devices/sparc64-softmmu/default.mak
index e50030a229..fa82f39a20 100644
--- a/configs/devices/sparc64-softmmu/default.mak
+++ b/configs/devices/sparc64-softmmu/default.mak
@@ -6,7 +6,6 @@
#CONFIG_SUNHME=n
#CONFIG_TEST_DEVICES=n
-# Boards:
-#
-CONFIG_SUN4U=y
-CONFIG_NIAGARA=y
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_SUN4U=n
+# CONFIG_NIAGARA=n
diff --git a/configs/devices/tricore-softmmu/default.mak b/configs/devices/tricore-softmmu/default.mak
index cb8fc286eb..c7ab542244 100644
--- a/configs/devices/tricore-softmmu/default.mak
+++ b/configs/devices/tricore-softmmu/default.mak
@@ -1,2 +1,5 @@
-CONFIG_TRICORE_TESTBOARD=y
-CONFIG_TRIBOARD=y
+# Default configuration for tricore-softmmu
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_TRICORE_TESTBOARD=n
+# CONFIG_TRIBOARD=n
diff --git a/configs/devices/xtensa-softmmu/default.mak b/configs/devices/xtensa-softmmu/default.mak
index 49e4c9da88..fbc3079a94 100644
--- a/configs/devices/xtensa-softmmu/default.mak
+++ b/configs/devices/xtensa-softmmu/default.mak
@@ -1,7 +1,10 @@
# Default configuration for Xtensa
-# Boards:
+# Uncomment the following lines to disable these optional devices:
#
-CONFIG_XTENSA_SIM=y
-CONFIG_XTENSA_VIRT=y
-CONFIG_XTENSA_XTFPGA=y
+#CONFIG_PCI_DEVICES=n
+
+# Boards are selected by default, uncomment to keep out of the build.
+# CONFIG_XTENSA_SIM=n
+# CONFIG_XTENSA_VIRT=n
+# CONFIG_XTENSA_XTFPGA=n
diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak
index 83c22391a6..84cb32dc2f 100644
--- a/configs/targets/aarch64-softmmu.mak
+++ b/configs/targets/aarch64-softmmu.mak
@@ -3,4 +3,5 @@ TARGET_BASE_ARCH=arm
TARGET_SUPPORTS_MTTCG=y
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml
+# needed by boot.c
TARGET_NEED_FDT=y
diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak
index 92c8349b96..bf390b7a8d 100644
--- a/configs/targets/arm-softmmu.mak
+++ b/configs/targets/arm-softmmu.mak
@@ -1,4 +1,5 @@
TARGET_ARCH=arm
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml
+# needed by boot.c
TARGET_NEED_FDT=y
diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak
index d61b507613..2ac69d5ba3 100644
--- a/configs/targets/i386-softmmu.mak
+++ b/configs/targets/i386-softmmu.mak
@@ -1,5 +1,4 @@
TARGET_ARCH=i386
TARGET_SUPPORTS_MTTCG=y
-TARGET_NEED_FDT=y
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_XML_FILES= gdb-xml/i386-32bit.xml
diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak
index f23780fdd8..84beb19b90 100644
--- a/configs/targets/loongarch64-softmmu.mak
+++ b/configs/targets/loongarch64-softmmu.mak
@@ -2,4 +2,5 @@ TARGET_ARCH=loongarch64
TARGET_BASE_ARCH=loongarch
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml
+# all boards require libfdt
TARGET_NEED_FDT=y
diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak
index e84c0cc728..eea266d4f3 100644
--- a/configs/targets/microblaze-softmmu.mak
+++ b/configs/targets/microblaze-softmmu.mak
@@ -1,5 +1,6 @@
TARGET_ARCH=microblaze
TARGET_BIG_ENDIAN=y
TARGET_SUPPORTS_MTTCG=y
+# needed by boot.c
TARGET_NEED_FDT=y
TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml
diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak
index 9b688036bd..77b968acad 100644
--- a/configs/targets/microblazeel-softmmu.mak
+++ b/configs/targets/microblazeel-softmmu.mak
@@ -1,4 +1,5 @@
TARGET_ARCH=microblaze
TARGET_SUPPORTS_MTTCG=y
+# needed by boot.c
TARGET_NEED_FDT=y
TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml
diff --git a/configs/targets/mips64el-softmmu.mak b/configs/targets/mips64el-softmmu.mak
index 8d9ab3ddc4..3864daa736 100644
--- a/configs/targets/mips64el-softmmu.mak
+++ b/configs/targets/mips64el-softmmu.mak
@@ -1,3 +1,2 @@
TARGET_ARCH=mips64
TARGET_BASE_ARCH=mips
-TARGET_NEED_FDT=y
diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak
index 432f855a30..0341cb2a6b 100644
--- a/configs/targets/or1k-softmmu.mak
+++ b/configs/targets/or1k-softmmu.mak
@@ -1,4 +1,5 @@
TARGET_ARCH=openrisc
TARGET_SUPPORTS_MTTCG=y
TARGET_BIG_ENDIAN=y
+# needed by boot.c and all boards
TARGET_NEED_FDT=y
diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak
index f3ea9c98f7..53120dab41 100644
--- a/configs/targets/ppc-softmmu.mak
+++ b/configs/targets/ppc-softmmu.mak
@@ -2,4 +2,3 @@ TARGET_ARCH=ppc
TARGET_BIG_ENDIAN=y
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml
-TARGET_NEED_FDT=y
diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak
index 1db8d8381d..40881d9396 100644
--- a/configs/targets/ppc64-softmmu.mak
+++ b/configs/targets/ppc64-softmmu.mak
@@ -4,4 +4,5 @@ TARGET_BIG_ENDIAN=y
TARGET_SUPPORTS_MTTCG=y
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml
+# all boards require libfdt
TARGET_NEED_FDT=y
diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak
index d8b71cddcd..338182d5b8 100644
--- a/configs/targets/riscv32-softmmu.mak
+++ b/configs/targets/riscv32-softmmu.mak
@@ -2,4 +2,5 @@ TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml
+# needed by boot.c
TARGET_NEED_FDT=y
diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak
index 7c0e7eeb42..f688ffa7bc 100644
--- a/configs/targets/riscv64-softmmu.mak
+++ b/configs/targets/riscv64-softmmu.mak
@@ -2,4 +2,5 @@ TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml
+# needed by boot.c
TARGET_NEED_FDT=y
diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak
index 0c458b2d07..706bbe6062 100644
--- a/configs/targets/rx-softmmu.mak
+++ b/configs/targets/rx-softmmu.mak
@@ -1,3 +1,4 @@
TARGET_ARCH=rx
TARGET_XML_FILES= gdb-xml/rx-core.xml
+# all boards require libfdt
TARGET_NEED_FDT=y
diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak
index c5f882e5ba..e12ac3dc59 100644
--- a/configs/targets/x86_64-softmmu.mak
+++ b/configs/targets/x86_64-softmmu.mak
@@ -1,6 +1,5 @@
TARGET_ARCH=x86_64
TARGET_BASE_ARCH=i386
TARGET_SUPPORTS_MTTCG=y
-TARGET_NEED_FDT=y
TARGET_KVM_HAVE_GUEST_DEBUG=y
TARGET_XML_FILES= gdb-xml/i386-64bit.xml
diff --git a/configure b/configure
index 1dca3d94c0..38ee257701 100755
--- a/configure
+++ b/configure
@@ -411,7 +411,9 @@ else
# Using uname is really broken, but it is just a fallback for architectures
# that are going to use TCI anyway
cpu=$(uname -m)
- echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'"
+ if test "$host_os" != "bogus"; then
+ echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'"
+ fi
fi
# Normalise host CPU name to the values used by Meson cross files and in source
@@ -762,7 +764,7 @@ for opt do
--*) meson_option_parse "$opt" "$optarg"
;;
# Pass through -Dxxxx options to meson
- -D*) meson_options="$meson_options $opt"
+ -D*) meson_option_add "$opt"
;;
esac
done
@@ -894,6 +896,13 @@ EOF
exit 0
fi
+# Now that we are sure that the user did not only want to print the --help
+# information, we should double-check that the C compiler really works:
+write_c_skeleton
+if ! compile_object ; then
+ error_exit "C compiler \"$cc\" either does not exist or does not work."
+fi
+
# Remove old dependency files to make sure that they get properly regenerated
rm -f ./*/config-devices.mak.d
diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index fab18113d4..371db97eb1 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -258,8 +258,9 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
NULL);
}
} else {
- uint32_t insn_opcode;
- insn_opcode = *((uint32_t *)qemu_plugin_insn_data(insn));
+ uint32_t insn_opcode = 0;
+ qemu_plugin_insn_data(insn, &insn_opcode, sizeof(insn_opcode));
+
char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"",
insn_vaddr, insn_opcode, insn_disas);
diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c
index 94bbc53820..9be67f7453 100644
--- a/contrib/plugins/howvec.c
+++ b/contrib/plugins/howvec.c
@@ -252,7 +252,7 @@ static struct qemu_plugin_scoreboard *find_counter(
{
int i;
uint64_t *cnt = NULL;
- uint32_t opcode;
+ uint32_t opcode = 0;
InsnClassExecCount *class = NULL;
/*
@@ -261,7 +261,7 @@ static struct qemu_plugin_scoreboard *find_counter(
* They would probably benefit from a more tailored plugin.
* However we can fall back to individual instruction counting.
*/
- opcode = *((uint32_t *)qemu_plugin_insn_data(insn));
+ qemu_plugin_insn_data(insn, &opcode, sizeof(opcode));
for (i = 0; !cnt && i < class_table_sz; i++) {
class = &class_table[i];
diff --git a/cpu-target.c b/cpu-target.c
index f88649c299..5af120e8aa 100644
--- a/cpu-target.c
+++ b/cpu-target.c
@@ -21,6 +21,7 @@
#include "qapi/error.h"
#include "exec/target_page.h"
+#include "exec/page-protection.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
#include "qemu/error-report.h"
diff --git a/disas/disas-common.c b/disas/disas-common.c
new file mode 100644
index 0000000000..de61f6d8a1
--- /dev/null
+++ b/disas/disas-common.c
@@ -0,0 +1,104 @@
+/*
+ * Common routines for disassembly.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "disas/disas.h"
+#include "disas/capstone.h"
+#include "hw/core/cpu.h"
+#include "exec/tswap.h"
+#include "disas-internal.h"
+
+
+/* Filled in by elfload.c. Simplistic, but will do for now. */
+struct syminfo *syminfos = NULL;
+
+/*
+ * Print an error message. We can assume that this is in response to
+ * an error return from {host,target}_read_memory.
+ */
+static void perror_memory(int status, bfd_vma memaddr,
+ struct disassemble_info *info)
+{
+ if (status != EIO) {
+ /* Can't happen. */
+ info->fprintf_func(info->stream, "Unknown error %d\n", status);
+ } else {
+ /* Address between memaddr and memaddr + len was out of bounds. */
+ info->fprintf_func(info->stream,
+ "Address 0x%" PRIx64 " is out of bounds.\n",
+ memaddr);
+ }
+}
+
+/* Print address in hex. */
+static void print_address(bfd_vma addr, struct disassemble_info *info)
+{
+ info->fprintf_func(info->stream, "0x%" PRIx64, addr);
+}
+
+/* Stub prevents some fruitless earching in optabs disassemblers. */
+static int symbol_at_address(bfd_vma addr, struct disassemble_info *info)
+{
+ return 1;
+}
+
+void disas_initialize_debug(CPUDebug *s)
+{
+ memset(s, 0, sizeof(*s));
+ s->info.arch = bfd_arch_unknown;
+ s->info.cap_arch = -1;
+ s->info.cap_insn_unit = 4;
+ s->info.cap_insn_split = 4;
+ s->info.memory_error_func = perror_memory;
+ s->info.symbol_at_address_func = symbol_at_address;
+}
+
+void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu)
+{
+ disas_initialize_debug(s);
+
+ s->cpu = cpu;
+ s->info.print_address_func = print_address;
+ if (target_words_bigendian()) {
+ s->info.endian = BFD_ENDIAN_BIG;
+ } else {
+ s->info.endian = BFD_ENDIAN_LITTLE;
+ }
+
+ CPUClass *cc = CPU_GET_CLASS(cpu);
+ if (cc->disas_set_info) {
+ cc->disas_set_info(cpu, &s->info);
+ }
+}
+
+int disas_gstring_printf(FILE *stream, const char *fmt, ...)
+{
+ /* We abuse the FILE parameter to pass a GString. */
+ GString *s = (GString *)stream;
+ int initial_len = s->len;
+ va_list va;
+
+ va_start(va, fmt);
+ g_string_append_vprintf(s, fmt, va);
+ va_end(va);
+
+ return s->len - initial_len;
+}
+
+/* Look up symbol for debugging purpose. Returns "" if unknown. */
+const char *lookup_symbol(uint64_t orig_addr)
+{
+ const char *symbol = "";
+ struct syminfo *s;
+
+ for (s = syminfos; s; s = s->next) {
+ symbol = s->lookup_symbol(s, orig_addr);
+ if (symbol[0] != '\0') {
+ break;
+ }
+ }
+
+ return symbol;
+}
diff --git a/disas/disas-host.c b/disas/disas-host.c
new file mode 100644
index 0000000000..8146fafe80
--- /dev/null
+++ b/disas/disas-host.c
@@ -0,0 +1,129 @@
+/*
+ * Routines for host instruction disassembly.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "disas/disas.h"
+#include "disas/capstone.h"
+#include "disas-internal.h"
+
+
+/*
+ * Get LENGTH bytes from info's buffer, at host address memaddr.
+ * Transfer them to myaddr.
+ */
+static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info)
+{
+ if (memaddr < info->buffer_vma
+ || memaddr + length > info->buffer_vma + info->buffer_length) {
+ /* Out of bounds. Use EIO because GDB uses it. */
+ return EIO;
+ }
+ memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
+ return 0;
+}
+
+/* Print address in hex, truncated to the width of a host virtual address. */
+static void host_print_address(bfd_vma addr, struct disassemble_info *info)
+{
+ info->fprintf_func(info->stream, "0x%" PRIxPTR, (uintptr_t)addr);
+}
+
+static void initialize_debug_host(CPUDebug *s)
+{
+ disas_initialize_debug(s);
+
+ s->info.read_memory_func = host_read_memory;
+ s->info.print_address_func = host_print_address;
+#if HOST_BIG_ENDIAN
+ s->info.endian = BFD_ENDIAN_BIG;
+#else
+ s->info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(CONFIG_TCG_INTERPRETER)
+ s->info.print_insn = print_insn_tci;
+#elif defined(__i386__)
+ s->info.mach = bfd_mach_i386_i386;
+ s->info.cap_arch = CS_ARCH_X86;
+ s->info.cap_mode = CS_MODE_32;
+ s->info.cap_insn_unit = 1;
+ s->info.cap_insn_split = 8;
+#elif defined(__x86_64__)
+ s->info.mach = bfd_mach_x86_64;
+ s->info.cap_arch = CS_ARCH_X86;
+ s->info.cap_mode = CS_MODE_64;
+ s->info.cap_insn_unit = 1;
+ s->info.cap_insn_split = 8;
+#elif defined(_ARCH_PPC)
+ s->info.cap_arch = CS_ARCH_PPC;
+# ifdef _ARCH_PPC64
+ s->info.cap_mode = CS_MODE_64;
+# endif
+#elif defined(__riscv)
+#if defined(_ILP32) || (__riscv_xlen == 32)
+ s->info.print_insn = print_insn_riscv32;
+#elif defined(_LP64)
+ s->info.print_insn = print_insn_riscv64;
+#else
+#error unsupported RISC-V ABI
+#endif
+#elif defined(__aarch64__)
+ s->info.cap_arch = CS_ARCH_ARM64;
+#elif defined(__alpha__)
+ s->info.print_insn = print_insn_alpha;
+#elif defined(__sparc__)
+ s->info.print_insn = print_insn_sparc;
+ s->info.mach = bfd_mach_sparc_v9b;
+#elif defined(__arm__)
+ /* TCG only generates code for arm mode. */
+ s->info.cap_arch = CS_ARCH_ARM;
+#elif defined(__MIPSEB__)
+ s->info.print_insn = print_insn_big_mips;
+#elif defined(__MIPSEL__)
+ s->info.print_insn = print_insn_little_mips;
+#elif defined(__m68k__)
+ s->info.print_insn = print_insn_m68k;
+#elif defined(__s390__)
+ s->info.cap_arch = CS_ARCH_SYSZ;
+ s->info.cap_insn_unit = 2;
+ s->info.cap_insn_split = 6;
+#elif defined(__hppa__)
+ s->info.print_insn = print_insn_hppa;
+#elif defined(__loongarch__)
+ s->info.print_insn = print_insn_loongarch;
+#endif
+}
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, const void *code, size_t size)
+{
+ uintptr_t pc;
+ int count;
+ CPUDebug s;
+
+ initialize_debug_host(&s);
+ s.info.fprintf_func = fprintf;
+ s.info.stream = out;
+ s.info.buffer = code;
+ s.info.buffer_vma = (uintptr_t)code;
+ s.info.buffer_length = size;
+ s.info.show_opcodes = true;
+
+ if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) {
+ return;
+ }
+
+ if (s.info.print_insn == NULL) {
+ s.info.print_insn = print_insn_od_host;
+ }
+ for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) {
+ fprintf(out, "0x%08" PRIxPTR ": ", pc);
+ count = s.info.print_insn(pc, &s.info);
+ fprintf(out, "\n");
+ if (count < 0) {
+ break;
+ }
+ }
+}
diff --git a/disas/disas-internal.h b/disas/disas-internal.h
index 84a01f126f..ed32e704cc 100644
--- a/disas/disas-internal.h
+++ b/disas/disas-internal.h
@@ -14,8 +14,12 @@ typedef struct CPUDebug {
CPUState *cpu;
} CPUDebug;
+void disas_initialize_debug(CPUDebug *s);
void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu);
int disas_gstring_printf(FILE *stream, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
+int print_insn_od_host(bfd_vma pc, disassemble_info *info);
+int print_insn_od_target(bfd_vma pc, disassemble_info *info);
+
#endif
diff --git a/disas/disas-mon.c b/disas/disas-mon.c
index 5d6d9aa02d..37bf16ac79 100644
--- a/disas/disas-mon.c
+++ b/disas/disas-mon.c
@@ -11,6 +11,19 @@
#include "hw/core/cpu.h"
#include "monitor/monitor.h"
+/*
+ * Get LENGTH bytes from info's buffer, at target address memaddr.
+ * Transfer them to myaddr.
+ */
+static int
+virtual_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info)
+{
+ CPUDebug *s = container_of(info, CPUDebug, info);
+ int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0);
+ return r ? EIO : 0;
+}
+
static int
physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info)
@@ -38,6 +51,8 @@ void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc,
if (is_physical) {
s.info.read_memory_func = physical_read_memory;
+ } else {
+ s.info.read_memory_func = virtual_read_memory;
}
s.info.buffer_vma = pc;
diff --git a/disas/disas-target.c b/disas/disas-target.c
new file mode 100644
index 0000000000..48f3a365dc
--- /dev/null
+++ b/disas/disas-target.c
@@ -0,0 +1,99 @@
+/*
+ * Routines for target instruction disassembly.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "disas/disas.h"
+#include "disas/capstone.h"
+#include "exec/translator.h"
+#include "disas-internal.h"
+
+
+static int translator_read_memory(bfd_vma memaddr, bfd_byte *myaddr,
+ int length, struct disassemble_info *info)
+{
+ const DisasContextBase *db = info->application_data;
+ return translator_st(db, myaddr, memaddr, length) ? 0 : EIO;
+}
+
+void target_disas(FILE *out, CPUState *cpu, const struct DisasContextBase *db)
+{
+ uint64_t code = db->pc_first;
+ size_t size = translator_st_len(db);
+ uint64_t pc;
+ int count;
+ CPUDebug s;
+
+ disas_initialize_debug_target(&s, cpu);
+ s.info.read_memory_func = translator_read_memory;
+ s.info.application_data = (void *)db;
+ s.info.fprintf_func = fprintf;
+ s.info.stream = out;
+ s.info.buffer_vma = code;
+ s.info.buffer_length = size;
+ s.info.show_opcodes = true;
+
+ if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) {
+ return;
+ }
+
+ if (s.info.print_insn == NULL) {
+ s.info.print_insn = print_insn_od_target;
+ }
+
+ for (pc = code; size > 0; pc += count, size -= count) {
+ fprintf(out, "0x%08" PRIx64 ": ", pc);
+ count = s.info.print_insn(pc, &s.info);
+ fprintf(out, "\n");
+ if (count < 0) {
+ break;
+ }
+ if (size < count) {
+ fprintf(out,
+ "Disassembler disagrees with translator over instruction "
+ "decoding\n"
+ "Please report this to qemu-devel@nongnu.org\n");
+ break;
+ }
+ }
+}
+
+#ifdef CONFIG_PLUGIN
+static void plugin_print_address(bfd_vma addr, struct disassemble_info *info)
+{
+ /* does nothing */
+}
+
+/*
+ * We should only be dissembling one instruction at a time here. If
+ * there is left over it usually indicates the front end has read more
+ * bytes than it needed.
+ */
+char *plugin_disas(CPUState *cpu, const DisasContextBase *db,
+ uint64_t addr, size_t size)
+{
+ CPUDebug s;
+ GString *ds = g_string_new(NULL);
+
+ disas_initialize_debug_target(&s, cpu);
+ s.info.read_memory_func = translator_read_memory;
+ s.info.application_data = (void *)db;
+ s.info.fprintf_func = disas_gstring_printf;
+ s.info.stream = (FILE *)ds; /* abuse this slot */
+ s.info.buffer_vma = addr;
+ s.info.buffer_length = size;
+ s.info.print_address_func = plugin_print_address;
+
+ if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) {
+ ; /* done */
+ } else if (s.info.print_insn) {
+ s.info.print_insn(addr, &s.info);
+ } else {
+ ; /* cannot disassemble -- return empty string */
+ }
+
+ /* Return the buffer, freeing the GString container. */
+ return g_string_free(ds, false);
+}
+#endif /* CONFIG_PLUGIN */
diff --git a/disas/disas.c b/disas/disas.c
deleted file mode 100644
index ec14715ecd..0000000000
--- a/disas/disas.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/* General "disassemble this chunk" code. Used for debugging. */
-#include "qemu/osdep.h"
-#include "disas/disas-internal.h"
-#include "elf.h"
-#include "qemu/qemu-print.h"
-#include "disas/disas.h"
-#include "disas/capstone.h"
-#include "hw/core/cpu.h"
-#include "exec/tswap.h"
-#include "exec/memory.h"
-
-/* Filled in by elfload.c. Simplistic, but will do for now. */
-struct syminfo *syminfos = NULL;
-
-/*
- * Get LENGTH bytes from info's buffer, at host address memaddr.
- * Transfer them to myaddr.
- */
-static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
- struct disassemble_info *info)
-{
- if (memaddr < info->buffer_vma
- || memaddr + length > info->buffer_vma + info->buffer_length) {
- /* Out of bounds. Use EIO because GDB uses it. */
- return EIO;
- }
- memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
- return 0;
-}
-
-/*
- * Get LENGTH bytes from info's buffer, at target address memaddr.
- * Transfer them to myaddr.
- */
-static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
- struct disassemble_info *info)
-{
- CPUDebug *s = container_of(info, CPUDebug, info);
- int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0);
- return r ? EIO : 0;
-}
-
-/*
- * Print an error message. We can assume that this is in response to
- * an error return from {host,target}_read_memory.
- */
-static void perror_memory(int status, bfd_vma memaddr,
- struct disassemble_info *info)
-{
- if (status != EIO) {
- /* Can't happen. */
- info->fprintf_func(info->stream, "Unknown error %d\n", status);
- } else {
- /* Address between memaddr and memaddr + len was out of bounds. */
- info->fprintf_func(info->stream,
- "Address 0x%" PRIx64 " is out of bounds.\n",
- memaddr);
- }
-}
-
-/* Print address in hex. */
-static void print_address(bfd_vma addr, struct disassemble_info *info)
-{
- info->fprintf_func(info->stream, "0x%" PRIx64, addr);
-}
-
-/* Print address in hex, truncated to the width of a host virtual address. */
-static void host_print_address(bfd_vma addr, struct disassemble_info *info)
-{
- print_address((uintptr_t)addr, info);
-}
-
-/* Stub prevents some fruitless earching in optabs disassemblers. */
-static int symbol_at_address(bfd_vma addr, struct disassemble_info *info)
-{
- return 1;
-}
-
-static int print_insn_objdump(bfd_vma pc, disassemble_info *info,
- const char *prefix)
-{
- int i, n = info->buffer_length;
- g_autofree uint8_t *buf = g_malloc(n);
-
- if (info->read_memory_func(pc, buf, n, info) == 0) {
- for (i = 0; i < n; ++i) {
- if (i % 32 == 0) {
- info->fprintf_func(info->stream, "\n%s: ", prefix);
- }
- info->fprintf_func(info->stream, "%02x", buf[i]);
- }
- } else {
- info->fprintf_func(info->stream, "unable to read memory");
- }
- return n;
-}
-
-static int print_insn_od_host(bfd_vma pc, disassemble_info *info)
-{
- return print_insn_objdump(pc, info, "OBJD-H");
-}
-
-static int print_insn_od_target(bfd_vma pc, disassemble_info *info)
-{
- return print_insn_objdump(pc, info, "OBJD-T");
-}
-
-static void initialize_debug(CPUDebug *s)
-{
- memset(s, 0, sizeof(*s));
- s->info.arch = bfd_arch_unknown;
- s->info.cap_arch = -1;
- s->info.cap_insn_unit = 4;
- s->info.cap_insn_split = 4;
- s->info.memory_error_func = perror_memory;
- s->info.symbol_at_address_func = symbol_at_address;
-}
-
-void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu)
-{
- initialize_debug(s);
-
- s->cpu = cpu;
- s->info.read_memory_func = target_read_memory;
- s->info.print_address_func = print_address;
- if (target_words_bigendian()) {
- s->info.endian = BFD_ENDIAN_BIG;
- } else {
- s->info.endian = BFD_ENDIAN_LITTLE;
- }
-
- CPUClass *cc = CPU_GET_CLASS(cpu);
- if (cc->disas_set_info) {
- cc->disas_set_info(cpu, &s->info);
- }
-}
-
-static void initialize_debug_host(CPUDebug *s)
-{
- initialize_debug(s);
-
- s->info.read_memory_func = host_read_memory;
- s->info.print_address_func = host_print_address;
-#if HOST_BIG_ENDIAN
- s->info.endian = BFD_ENDIAN_BIG;
-#else
- s->info.endian = BFD_ENDIAN_LITTLE;
-#endif
-#if defined(CONFIG_TCG_INTERPRETER)
- s->info.print_insn = print_insn_tci;
-#elif defined(__i386__)
- s->info.mach = bfd_mach_i386_i386;
- s->info.cap_arch = CS_ARCH_X86;
- s->info.cap_mode = CS_MODE_32;
- s->info.cap_insn_unit = 1;
- s->info.cap_insn_split = 8;
-#elif defined(__x86_64__)
- s->info.mach = bfd_mach_x86_64;
- s->info.cap_arch = CS_ARCH_X86;
- s->info.cap_mode = CS_MODE_64;
- s->info.cap_insn_unit = 1;
- s->info.cap_insn_split = 8;
-#elif defined(_ARCH_PPC)
- s->info.cap_arch = CS_ARCH_PPC;
-# ifdef _ARCH_PPC64
- s->info.cap_mode = CS_MODE_64;
-# endif
-#elif defined(__riscv)
-#if defined(_ILP32) || (__riscv_xlen == 32)
- s->info.print_insn = print_insn_riscv32;
-#elif defined(_LP64)
- s->info.print_insn = print_insn_riscv64;
-#else
-#error unsupported RISC-V ABI
-#endif
-#elif defined(__aarch64__)
- s->info.cap_arch = CS_ARCH_ARM64;
-#elif defined(__alpha__)
- s->info.print_insn = print_insn_alpha;
-#elif defined(__sparc__)
- s->info.print_insn = print_insn_sparc;
- s->info.mach = bfd_mach_sparc_v9b;
-#elif defined(__arm__)
- /* TCG only generates code for arm mode. */
- s->info.cap_arch = CS_ARCH_ARM;
-#elif defined(__MIPSEB__)
- s->info.print_insn = print_insn_big_mips;
-#elif defined(__MIPSEL__)
- s->info.print_insn = print_insn_little_mips;
-#elif defined(__m68k__)
- s->info.print_insn = print_insn_m68k;
-#elif defined(__s390__)
- s->info.cap_arch = CS_ARCH_SYSZ;
- s->info.cap_insn_unit = 2;
- s->info.cap_insn_split = 6;
-#elif defined(__hppa__)
- s->info.print_insn = print_insn_hppa;
-#elif defined(__loongarch__)
- s->info.print_insn = print_insn_loongarch;
-#endif
-}
-
-/* Disassemble this for me please... (debugging). */
-void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size)
-{
- uint64_t pc;
- int count;
- CPUDebug s;
-
- disas_initialize_debug_target(&s, cpu);
- s.info.fprintf_func = fprintf;
- s.info.stream = out;
- s.info.buffer_vma = code;
- s.info.buffer_length = size;
- s.info.show_opcodes = true;
-
- if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) {
- return;
- }
-
- if (s.info.print_insn == NULL) {
- s.info.print_insn = print_insn_od_target;
- }
-
- for (pc = code; size > 0; pc += count, size -= count) {
- fprintf(out, "0x%08" PRIx64 ": ", pc);
- count = s.info.print_insn(pc, &s.info);
- fprintf(out, "\n");
- if (count < 0) {
- break;
- }
- if (size < count) {
- fprintf(out,
- "Disassembler disagrees with translator over instruction "
- "decoding\n"
- "Please report this to qemu-devel@nongnu.org\n");
- break;
- }
- }
-}
-
-int disas_gstring_printf(FILE *stream, const char *fmt, ...)
-{
- /* We abuse the FILE parameter to pass a GString. */
- GString *s = (GString *)stream;
- int initial_len = s->len;
- va_list va;
-
- va_start(va, fmt);
- g_string_append_vprintf(s, fmt, va);
- va_end(va);
-
- return s->len - initial_len;
-}
-
-static void plugin_print_address(bfd_vma addr, struct disassemble_info *info)
-{
- /* does nothing */
-}
-
-
-/*
- * We should only be dissembling one instruction at a time here. If
- * there is left over it usually indicates the front end has read more
- * bytes than it needed.
- */
-char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size)
-{
- CPUDebug s;
- GString *ds = g_string_new(NULL);
-
- disas_initialize_debug_target(&s, cpu);
- s.info.fprintf_func = disas_gstring_printf;
- s.info.stream = (FILE *)ds; /* abuse this slot */
- s.info.buffer_vma = addr;
- s.info.buffer_length = size;
- s.info.print_address_func = plugin_print_address;
-
- if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) {
- ; /* done */
- } else if (s.info.print_insn) {
- s.info.print_insn(addr, &s.info);
- } else {
- ; /* cannot disassemble -- return empty string */
- }
-
- /* Return the buffer, freeing the GString container. */
- return g_string_free(ds, false);
-}
-
-/* Disassemble this for me please... (debugging). */
-void disas(FILE *out, const void *code, size_t size)
-{
- uintptr_t pc;
- int count;
- CPUDebug s;
-
- initialize_debug_host(&s);
- s.info.fprintf_func = fprintf;
- s.info.stream = out;
- s.info.buffer = code;
- s.info.buffer_vma = (uintptr_t)code;
- s.info.buffer_length = size;
- s.info.show_opcodes = true;
-
- if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) {
- return;
- }
-
- if (s.info.print_insn == NULL) {
- s.info.print_insn = print_insn_od_host;
- }
- for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) {
- fprintf(out, "0x%08" PRIxPTR ": ", pc);
- count = s.info.print_insn(pc, &s.info);
- fprintf(out, "\n");
- if (count < 0) {
- break;
- }
- }
-
-}
-
-/* Look up symbol for debugging purpose. Returns "" if unknown. */
-const char *lookup_symbol(uint64_t orig_addr)
-{
- const char *symbol = "";
- struct syminfo *s;
-
- for (s = syminfos; s; s = s->next) {
- symbol = s->lookup_symbol(s, orig_addr);
- if (symbol[0] != '\0') {
- break;
- }
- }
-
- return symbol;
-}
diff --git a/disas/meson.build b/disas/meson.build
index 5c8073beb3..20d6aef9a7 100644
--- a/disas/meson.build
+++ b/disas/meson.build
@@ -14,7 +14,11 @@ common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c'))
common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c'))
common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c'))
common_ss.add(when: capstone, if_true: [files('capstone.c'), capstone])
-common_ss.add(files('disas.c'))
-
+common_ss.add(when: 'CONFIG_TCG', if_true: files(
+ 'disas-host.c',
+ 'disas-target.c',
+ 'objdump.c'
+))
+common_ss.add(files('disas-common.c'))
system_ss.add(files('disas-mon.c'))
specific_ss.add(capstone)
diff --git a/disas/objdump.c b/disas/objdump.c
new file mode 100644
index 0000000000..9859f23419
--- /dev/null
+++ b/disas/objdump.c
@@ -0,0 +1,37 @@
+/*
+ * Dump disassembly as text, for processing by scripts/disas-objdump.pl.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "disas-internal.h"
+
+
+static int print_insn_objdump(bfd_vma pc, disassemble_info *info,
+ const char *prefix)
+{
+ int i, n = info->buffer_length;
+ g_autofree uint8_t *buf = g_malloc(n);
+
+ if (info->read_memory_func(pc, buf, n, info) == 0) {
+ for (i = 0; i < n; ++i) {
+ if (i % 32 == 0) {
+ info->fprintf_func(info->stream, "\n%s: ", prefix);
+ }
+ info->fprintf_func(info->stream, "%02x", buf[i]);
+ }
+ } else {
+ info->fprintf_func(info->stream, "unable to read memory");
+ }
+ return n;
+}
+
+int print_insn_od_host(bfd_vma pc, disassemble_info *info)
+{
+ return print_insn_objdump(pc, info, "OBJD-H");
+}
+
+int print_insn_od_target(bfd_vma pc, disassemble_info *info)
+{
+ return print_insn_objdump(pc, info, "OBJD-T");
+}
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 7b8aafa15b..40585ca7d5 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -61,6 +61,12 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is
marked deprecated since 9.0, users have to ensure that all the topology members
described with -smp are supported by the target machine.
+``-runas`` (since 9.1)
+----------------------
+
+Use ``-run-with user=..`` instead.
+
+
User-mode emulator command line arguments
-----------------------------------------
@@ -194,6 +200,15 @@ in the QEMU object model anymore. ``power5+``, ``power5+_v2.1``,
an alias, but for consistency these will get removed in a future
release, too. Use ``power5p_v2.1`` and ``power7p_v2.1`` instead.
+``Sun-UltraSparc-IIIi+`` and ``Sun-UltraSparc-IV+`` CPU names (since 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+The character "+" in device (and thus also CPU) names is not allowed
+in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and
+``Sun-UltraSparc-IV+`` are currently still supported via a workaround,
+but for consistency these will get removed in a future release, too.
+Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead.
+
CRIS CPU architecture (since 9.0)
'''''''''''''''''''''''''''''''''
@@ -249,6 +264,14 @@ dropping the ``cheetah`` OMAP1 board, because we don't have any
test images for it and don't know of anybody who does; the ``sx1``
and ``sx1-v1`` OMAP1 machines remain supported for now.
+PPC 405 ``ref405ep`` machine (since 9.1)
+''''''''''''''''''''''''''''''''''''''''
+
+The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware
+images are not available, OpenWRT dropped support in 2019, U-Boot in
+2017, Linux also is dropping support in 2024. It is time to let go of
+this ancient hardware and focus on newer CPUs and platforms.
+
Backend options
---------------
@@ -409,6 +432,14 @@ Backend ``memory`` (since 9.0)
CPU device properties
'''''''''''''''''''''
+``pcommit`` on x86 (since 9.1)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The PCOMMIT instruction was never included in any physical processor.
+It was implemented as a no-op instruction in TCG up to QEMU 9.0, but
+only with ``-cpu max`` (which does not guarantee migration compatibility
+across versions).
+
``pmu-num=n`` on RISC-V CPUs (since 8.2)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -468,44 +499,13 @@ option).
Migration
---------
-``skipped`` MigrationStats field (since 8.1)
-''''''''''''''''''''''''''''''''''''''''''''
-
-``skipped`` field in Migration stats has been deprecated. It hasn't
-been used for more than 10 years.
-
-``inc`` migrate command option (since 8.2)
-''''''''''''''''''''''''''''''''''''''''''
-
-Use blockdev-mirror with NBD instead.
-
-As an intermediate step the ``inc`` functionality can be achieved by
-setting the ``block-incremental`` migration parameter to ``true``.
-But this parameter is also deprecated.
-
-``blk`` migrate command option (since 8.2)
-''''''''''''''''''''''''''''''''''''''''''
-
-Use blockdev-mirror with NBD instead.
-
-As an intermediate step the ``blk`` functionality can be achieved by
-setting the ``block`` migration capability to ``true``. But this
-capability is also deprecated.
-
-block migration (since 8.2)
-'''''''''''''''''''''''''''
-
-Block migration is too inflexible. It needs to migrate all block
-devices or none.
-
-Please see "QMP invocation for live storage migration with
-``blockdev-mirror`` + NBD" in docs/interop/live-block-operations.rst
-for a detailed explanation.
-
-old compression method (since 8.2)
-''''''''''''''''''''''''''''''''''
+``fd:`` URI when used for file migration (since 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''
-Compression method fails too much. Too many races. We are going to
-remove it if nobody fixes it. For starters, migration-test
-compression tests are disabled because they fail randomly. If you need
-compression, use multifd compression methods.
+The ``fd:`` URI can currently provide a file descriptor that
+references either a socket or a plain file. These are two different
+types of migration. In order to reduce ambiguity, the ``fd:`` URI
+usage of providing a file descriptor to a plain file has been
+deprecated in favor of explicitly using the ``file:`` URI with the
+file descriptor being passed as an ``fdset``. Refer to the ``add-fd``
+command documentation for details on the ``fdset`` usage.
diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst
index 53ca08aba9..fba0cfb0b0 100644
--- a/docs/about/removed-features.rst
+++ b/docs/about/removed-features.rst
@@ -505,6 +505,11 @@ configurations (e.g. -smp 8,sockets=0) is removed since 9.0, users have
to ensure that all the topology members described with -smp are greater
than zero.
+``-global migration.decompress-error-check`` (removed in 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Removed along with the ``compression`` migration capability.
+
User-mode emulator command line arguments
-----------------------------------------
@@ -614,6 +619,58 @@ was superseded by ``sections``.
Member ``section-size`` in the return value of ``query-sgx-capabilities``
was superseded by ``sections``.
+``query-migrate`` return value member ``skipped`` (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Member ``skipped`` of the ``MigrationStats`` struct hasn't been used
+for more than 10 years. Removed with no replacement.
+
+``migrate`` command option ``inc`` (removed in 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use blockdev-mirror with NBD instead. See "QMP invocation for live
+storage migration with ``blockdev-mirror`` + NBD" in
+docs/interop/live-block-operations.rst for a detailed explanation.
+
+``migrate`` command option ``blk`` (removed in 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use blockdev-mirror with NBD instead. See "QMP invocation for live
+storage migration with ``blockdev-mirror`` + NBD" in
+docs/interop/live-block-operations.rst for a detailed explanation.
+
+``migrate-set-capabilities`` ``block`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Block migration has been removed. For a replacement, see "QMP
+invocation for live storage migration with ``blockdev-mirror`` + NBD"
+in docs/interop/live-block-operations.rst.
+
+``migrate-set-parameter`` ``compress-level`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead.
+
+``migrate-set-parameter`` ``compress-threads`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-channels`` instead.
+
+``migrate-set-parameter`` ``compress-wait-thread`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Removed with no replacement.
+
+``migrate-set-parameter`` ``decompress-threads`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-channels`` instead.
+
+``migrate-set-capability`` ``compress`` option (removed in 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-compression`` instead.
+
Human Monitor Protocol (HMP) commands
-------------------------------------
@@ -674,6 +731,52 @@ This command didn't produce any output already. Removed with no replacement.
The ``singlestep`` command has been replaced by the ``one-insn-per-tb``
command, which has the same behaviour but a less misleading name.
+``migrate`` command ``-i`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use blockdev-mirror with NBD instead. See "QMP invocation for live
+storage migration with ``blockdev-mirror`` + NBD" in
+docs/interop/live-block-operations.rst for a detailed explanation.
+
+``migrate`` command ``-b`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use blockdev-mirror with NBD instead. See "QMP invocation for live
+storage migration with ``blockdev-mirror`` + NBD" in
+docs/interop/live-block-operations.rst for a detailed explanation.
+
+``migrate_set_capability`` ``block`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Block migration has been removed. For a replacement, see "QMP
+invocation for live storage migration with ``blockdev-mirror`` + NBD"
+in docs/interop/live-block-operations.rst.
+
+``migrate_set_parameter`` ``compress-level`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead.
+
+``migrate_set_parameter`` ``compress-threads`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-channels`` instead.
+
+``migrate_set_parameter`` ``compress-wait-thread`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Removed with no replacement.
+
+``migrate_set_parameter`` ``decompress-threads`` option (removed in 9.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-channels`` instead.
+
+``migrate_set_capability`` ``compress`` option (removed in 9.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Use ``multifd-compression`` instead.
+
Host Architectures
------------------
diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst
index ccb9a46bd7..52d4b905f6 100644
--- a/docs/devel/kconfig.rst
+++ b/docs/devel/kconfig.rst
@@ -211,6 +211,8 @@ declares its dependencies in different ways:
config SUN4M
bool
+ default y
+ depends on SPARC && !SPARC64
imply TCX
imply CG3
select CS4231
@@ -228,8 +230,16 @@ declares its dependencies in different ways:
directives. A device should be listed under ``select`` if the board
cannot be started at all without it. It should be listed under
``imply`` if (depending on the QEMU command line) the board may or
- may not be started without it. Boards also default to false; they are
- enabled by the ``default-configs/*.mak`` for the target they apply to.
+ may not be started without it. Boards default to true, but also
+ have a ``depends on`` clause to limit them to the appropriate targets.
+ For some targets, not all boards may be supported by hardware
+ virtualization, in which case they also depend on the ``TCG`` symbol,
+ Other symbols that are commonly used as dependencies for boards
+ include libraries (such as ``FDT``) or ``TARGET_BIG_ENDIAN``
+ (possibly negated).
+
+ Boards are listed for convenience in the ``default-configs/*.mak``
+ for the target they apply to.
**internal elements**
diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst
index 54385a23e5..495cdcb112 100644
--- a/docs/devel/migration/main.rst
+++ b/docs/devel/migration/main.rst
@@ -454,7 +454,7 @@ Examples of such API functions are:
Iterative device migration
--------------------------
-Some devices, such as RAM, Block storage or certain platform devices,
+Some devices, such as RAM or certain platform devices,
have large amounts of data that would mean that the CPUs would be
paused for too long if they were sent in one section. For these
devices an *iterative* approach is taken.
diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
index 8d428c64b0..f270b494f0 100644
--- a/docs/sphinx/qapidoc.py
+++ b/docs/sphinx/qapidoc.py
@@ -145,22 +145,22 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
term.extend(self._nodes_for_ifcond(member.ifcond))
return term
- def _nodes_for_variant_when(self, variants, variant):
+ def _nodes_for_variant_when(self, branches, variant):
"""Return list of Text, literal nodes for variant 'when' clause
Return a list of doctree nodes which give text like
'when tagname is variant (If: ...)' suitable for use in
- the 'variants' part of a definition list.
+ the 'branches' part of a definition list.
"""
term = [nodes.Text(' when '),
- nodes.literal('', variants.tag_member.name),
+ nodes.literal('', branches.tag_member.name),
nodes.Text(' is '),
nodes.literal('', '"%s"' % variant.name)]
if variant.ifcond.is_present():
term.extend(self._nodes_for_ifcond(variant.ifcond))
return term
- def _nodes_for_members(self, doc, what, base=None, variants=None):
+ def _nodes_for_members(self, doc, what, base=None, branches=None):
"""Return list of doctree nodes for the table of members"""
dlnode = nodes.definition_list()
for section in doc.args.values():
@@ -178,14 +178,14 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
nodes.literal('', base.doc_type())],
None)
- if variants:
- for v in variants.variants:
+ if branches:
+ for v in branches.variants:
if v.type.name == 'q_empty':
continue
assert not v.type.is_implicit()
term = [nodes.Text('The members of '),
nodes.literal('', v.type.doc_type())]
- term.extend(self._nodes_for_variant_when(variants, v))
+ term.extend(self._nodes_for_variant_when(branches, v))
dlnode += self._make_dlitem(term, None)
if not dlnode.children:
@@ -308,17 +308,18 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
+ self._nodes_for_if_section(ifcond))
def visit_object_type(self, name, info, ifcond, features,
- base, members, variants):
+ base, members, branches):
doc = self._cur_doc
if base and base.is_implicit():
base = None
self._add_doc('Object',
- self._nodes_for_members(doc, 'Members', base, variants)
+ self._nodes_for_members(doc, 'Members', base, branches)
+ self._nodes_for_features(doc)
+ self._nodes_for_sections(doc)
+ self._nodes_for_if_section(ifcond))
- def visit_alternate_type(self, name, info, ifcond, features, variants):
+ def visit_alternate_type(self, name, info, ifcond, features,
+ alternatives):
doc = self._cur_doc
self._add_doc('Alternate',
self._nodes_for_members(doc, 'Members')
diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst
index fbec1da6a1..44eec3f1c3 100644
--- a/docs/system/arm/raspi.rst
+++ b/docs/system/arm/raspi.rst
@@ -40,7 +40,6 @@ Implemented devices
Missing devices
---------------
- * Analog to Digital Converter (ADC)
* Pulse Width Modulation (PWM)
* PCIE Root Port (raspi4b)
* GENET Ethernet Controller (raspi4b)
diff --git a/docs/system/target-sparc.rst b/docs/system/target-sparc.rst
index 9ec8c90c14..4116cac493 100644
--- a/docs/system/target-sparc.rst
+++ b/docs/system/target-sparc.rst
@@ -27,6 +27,11 @@ architecture machines:
The emulation is somewhat complete. SMP up to 16 CPUs is supported, but
Linux limits the number of usable CPUs to 4.
+The list of available CPUs can be viewed by starting QEMU with ``-cpu help``.
+Optional boolean features can be added with a "+" in front of the feature name,
+or disabled with a "-" in front of the name, for example
+``-cpu TI-SuperSparc-II,+float128``.
+
QEMU emulates the following sun4m peripherals:
- IOMMU
@@ -55,8 +60,5 @@ OpenBIOS is a free (GPL v2) portable firmware implementation. The goal
is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware)
compliant firmware.
-A sample Linux 2.6 series kernel and ram disk image are available on the
-QEMU web site. There are still issues with NetBSD and OpenBSD, but most
-kernel versions work. Please note that currently older Solaris kernels
-don't work probably due to interface issues between OpenBIOS and
-Solaris.
+Please note that currently older Solaris kernels don't work; this is probably
+due to interface issues between OpenBIOS and Solaris.
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 9c2b8b5d0a..b3574997ea 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -32,6 +32,7 @@
#include "exec/gdbstub.h"
#include "gdbstub/syscalls.h"
#ifdef CONFIG_USER_ONLY
+#include "accel/tcg/vcpu-state.h"
#include "gdbstub/user.h"
#else
#include "hw/cpu/cluster.h"
@@ -1661,7 +1662,7 @@ static void handle_query_supported(GArray *params, void *user_ctx)
#if defined(CONFIG_USER_ONLY)
#if defined(CONFIG_LINUX)
- if (gdbserver_state.c_cpu->opaque) {
+ if (get_task_state(gdbserver_state.c_cpu)) {
g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+");
}
g_string_append(gdbserver_state.str_buf, ";QCatchSyscalls+");
diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
index 6646684a4c..a9c6c64512 100644
--- a/gdbstub/user-target.c
+++ b/gdbstub/user-target.c
@@ -216,7 +216,7 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx)
{
TaskState *ts;
- ts = gdbserver_state.c_cpu->opaque;
+ ts = get_task_state(gdbserver_state.c_cpu);
g_string_printf(gdbserver_state.str_buf,
"Text=" TARGET_ABI_FMT_lx
";Data=" TARGET_ABI_FMT_lx
@@ -252,7 +252,7 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx)
offset = get_param(params, 0)->val_ul;
len = get_param(params, 1)->val_ul;
- ts = gdbserver_state.c_cpu->opaque;
+ ts = get_task_state(gdbserver_state.c_cpu);
saved_auxv = ts->info->saved_auxv;
auxv_len = ts->info->auxv_len;
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 2e2a3bcf98..06746f0afc 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -909,26 +909,23 @@ ERST
{
.name = "migrate",
- .args_type = "detach:-d,blk:-b,inc:-i,resume:-r,uri:s",
- .params = "[-d] [-b] [-i] [-r] uri",
+ .args_type = "detach:-d,resume:-r,uri:s",
+ .params = "[-d] [-r] uri",
.help = "migrate to URI (using -d to not wait for completion)"
- "\n\t\t\t -b for migration without shared storage with"
- " full copy of disk\n\t\t\t -i for migration without "
- "shared storage with incremental copy of disk "
- "(base image shared between src and destination)"
- "\n\t\t\t -r to resume a paused migration",
+ "\n\t\t\t -r to resume a paused postcopy migration",
.cmd = hmp_migrate,
},
SRST
-``migrate [-d] [-b] [-i]`` *uri*
- Migrate to *uri* (using -d to not wait for completion).
+``migrate [-d] [-r]`` *uri*
+ Migrate the VM to *uri*.
- ``-b``
- for migration with full copy of disk
- ``-i``
- for migration with incremental copy of disk (base image is shared)
+ ``-d``
+ Start the migration process, but do not wait for its completion. To
+ query an ongoing migration process, use "info migrate".
+ ``-r``
+ Resume a paused postcopy migration.
ERST
{
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c
index 4aa9c8c736..a3ac53f989 100644
--- a/hw/9pfs/xen-9p-backend.c
+++ b/hw/9pfs/xen-9p-backend.c
@@ -513,7 +513,7 @@ static void xen_9pfs_alloc(struct XenLegacyDevice *xendev)
xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER);
}
-struct XenDevOps xen_9pfs_ops = {
+static struct XenDevOps xen_9pfs_ops = {
.size = sizeof(Xen9pfsDev),
.flags = DEVOPS_FLAG_NEED_GNTDEV,
.alloc = xen_9pfs_alloc,
@@ -522,3 +522,9 @@ struct XenDevOps xen_9pfs_ops = {
.disconnect = xen_9pfs_disconnect,
.free = xen_9pfs_free,
};
+
+static void xen_9pfs_register_backend(void)
+{
+ xen_be_register("9pfs", &xen_9pfs_ops);
+}
+xen_backend_init(xen_9pfs_register_backend);
diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig
index 9af650c94e..7f3455ce1e 100644
--- a/hw/alpha/Kconfig
+++ b/hw/alpha/Kconfig
@@ -1,5 +1,7 @@
config DP264
bool
+ default y
+ depends on ALPHA
imply PCI_DEVICES
imply TEST_DEVICES
imply E1000_PCI
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index fe1f9643bd..8b97683a45 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -1,5 +1,7 @@
config ARM_VIRT
bool
+ default y
+ depends on ARM
imply PCI_DEVICES
imply TEST_DEVICES
imply VFIO_AMD_XGBE
@@ -13,6 +15,7 @@ config ARM_VIRT
select ACPI
select ARM_SMMUV3
select GPIO_KEY
+ select DEVICE_TREE
select FW_CFG_DMA
select PCI_EXPRESS
select PCI_EXPRESS_GENERIC_BRIDGE
@@ -263,6 +266,7 @@ config SBSA_REF
default y
depends on TCG && AARCH64
imply PCI_DEVICES
+ select DEVICE_TREE
select AHCI
select ARM_SMMUV3
select GPIO_KEY
@@ -345,6 +349,7 @@ config VEXPRESS
bool
default y
depends on TCG && ARM
+ select DEVICE_TREE
select A9MPCORE
select A15MPCORE
select ARM_MPTIMER
@@ -490,6 +495,7 @@ config XLNX_ZYNQMP_ARM
select CPU_CLUSTER
select DDC
select DPCD
+ select DEVICE_TREE
select SDHCI
select SSI
select SSI_M25P80
@@ -507,6 +513,7 @@ config XLNX_VERSAL
depends on TCG && AARCH64
select ARM_GIC
select CPU_CLUSTER
+ select DEVICE_TREE
select PL011
select CADENCE
select VIRTIO_MMIO
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 84ea6a807a..d480a7da02 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -347,13 +347,13 @@ static void set_kernel_args_old(const struct arm_boot_info *info,
WRITE_WORD(p, info->ram_size / 4096);
/* ramdisk_size */
WRITE_WORD(p, 0);
-#define FLAG_READONLY 1
-#define FLAG_RDLOAD 4
-#define FLAG_RDPROMPT 8
+#define FLAG_READONLY 1
+#define FLAG_RDLOAD 4
+#define FLAG_RDPROMPT 8
/* flags */
WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
/* rootdev */
- WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
+ WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
/* video_num_cols */
WRITE_WORD(p, 0);
/* video_num_rows */
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 6808135c1f..aefde0c69a 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -1,5 +1,5 @@
arm_ss = ss.source_set()
-arm_ss.add(files('boot.c'), fdt)
+arm_ss.add(files('boot.c'))
arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c'))
arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c'))
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 9f2d96c733..cb7791301b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -487,9 +487,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
/* CPUs */
for (i = 0; i < nc->num_cpus; i++) {
- object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
- arm_build_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
- &error_abort);
object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
NPCM7XX_GIC_CPU_IF_ADDR, &error_abort);
object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
index 6a2ee085c0..7d09800d1f 100644
--- a/hw/audio/virtio-snd.c
+++ b/hw/audio/virtio-snd.c
@@ -19,7 +19,7 @@
#include "qemu/iov.h"
#include "qemu/log.h"
#include "qemu/error-report.h"
-#include "include/qemu/lockable.h"
+#include "qemu/lockable.h"
#include "exec/tswap.h"
#include "sysemu/runstate.h"
#include "trace.h"
diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig
index d31298c3cc..b29937be41 100644
--- a/hw/avr/Kconfig
+++ b/hw/avr/Kconfig
@@ -5,5 +5,8 @@ config AVR_ATMEGA_MCU
select AVR_POWER
config ARDUINO
+ bool
+ default y
+ depends on AVR
select AVR_ATMEGA_MCU
select UNIMP
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 1bda8424b9..c8f1cf5a87 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -518,10 +518,6 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
break;
case 0xe8: /* Write to buffer */
trace_pflash_write(pfl->name, "write to buffer");
- /* FIXME should save @offset, @width for case 1+ */
- qemu_log_mask(LOG_UNIMP,
- "%s: Write to buffer emulation is flawed\n",
- __func__);
pfl->status |= 0x80; /* Ready! */
break;
case 0xf0: /* Probe for AMD flash */
@@ -574,7 +570,6 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
}
pfl->counter = value;
pfl->wcycle++;
- pflash_blk_write_start(pfl, offset);
break;
case 0x60:
if (cmd == 0xd0) {
@@ -605,6 +600,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
switch (pfl->cmd) {
case 0xe8: /* Block write */
/* FIXME check @offset, @width */
+ if (pfl->blk_offset == -1 && pfl->counter) {
+ pflash_blk_write_start(pfl, offset);
+ }
if (!pfl->ro && (pfl->blk_offset != -1)) {
pflash_data_write(pfl, offset, value, width, be);
} else {
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 6848bddb4e..c2ef4c137e 100644
--- a/hw/char/omap_uart.c
+++ b/hw/char/omap_uart.c
@@ -61,7 +61,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base,
s->fclk = fclk;
s->irq = irq;
s->serial = serial_mm_init(get_system_memory(), base, 2, irq,
- omap_clk_getrate(fclk)/16,
+ omap_clk_getrate(fclk) / 16,
chr ?: qemu_chr_new(label, "null", NULL),
DEVICE_NATIVE_ENDIAN);
return s;
@@ -76,27 +76,27 @@ static uint64_t omap_uart_read(void *opaque, hwaddr addr, unsigned size)
}
switch (addr) {
- case 0x20: /* MDR1 */
+ case 0x20: /* MDR1 */
return s->mdr[0];
- case 0x24: /* MDR2 */
+ case 0x24: /* MDR2 */
return s->mdr[1];
- case 0x40: /* SCR */
+ case 0x40: /* SCR */
return s->scr;
- case 0x44: /* SSR */
+ case 0x44: /* SSR */
return 0x0;
- case 0x48: /* EBLR (OMAP2) */
+ case 0x48: /* EBLR (OMAP2) */
return s->eblr;
- case 0x4C: /* OSC_12M_SEL (OMAP1) */
+ case 0x4C: /* OSC_12M_SEL (OMAP1) */
return s->clksel;
- case 0x50: /* MVR */
+ case 0x50: /* MVR */
return 0x30;
- case 0x54: /* SYSC (OMAP2) */
+ case 0x54: /* SYSC (OMAP2) */
return s->syscontrol;
- case 0x58: /* SYSS (OMAP2) */
+ case 0x58: /* SYSS (OMAP2) */
return 1;
- case 0x5c: /* WER (OMAP2) */
+ case 0x5c: /* WER (OMAP2) */
return s->wkup;
- case 0x60: /* CFPS (OMAP2) */
+ case 0x60: /* CFPS (OMAP2) */
return s->cfps;
}
@@ -115,35 +115,36 @@ static void omap_uart_write(void *opaque, hwaddr addr,
}
switch (addr) {
- case 0x20: /* MDR1 */
+ case 0x20: /* MDR1 */
s->mdr[0] = value & 0x7f;
break;
- case 0x24: /* MDR2 */
+ case 0x24: /* MDR2 */
s->mdr[1] = value & 0xff;
break;
- case 0x40: /* SCR */
+ case 0x40: /* SCR */
s->scr = value & 0xff;
break;
- case 0x48: /* EBLR (OMAP2) */
+ case 0x48: /* EBLR (OMAP2) */
s->eblr = value & 0xff;
break;
- case 0x4C: /* OSC_12M_SEL (OMAP1) */
+ case 0x4C: /* OSC_12M_SEL (OMAP1) */
s->clksel = value & 1;
break;
- case 0x44: /* SSR */
- case 0x50: /* MVR */
- case 0x58: /* SYSS (OMAP2) */
+ case 0x44: /* SSR */
+ case 0x50: /* MVR */
+ case 0x58: /* SYSS (OMAP2) */
OMAP_RO_REG(addr);
break;
- case 0x54: /* SYSC (OMAP2) */
+ case 0x54: /* SYSC (OMAP2) */
s->syscontrol = value & 0x1d;
- if (value & 2)
+ if (value & 2) {
omap_uart_reset(s);
+ }
break;
- case 0x5c: /* WER (OMAP2) */
+ case 0x5c: /* WER (OMAP2) */
s->wkup = value & 0x7f;
break;
- case 0x60: /* CFPS (OMAP2) */
+ case 0x60: /* CFPS (OMAP2) */
s->cfps = value & 0xff;
break;
default:
diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c
index 02f666308c..fc5dcac0c4 100644
--- a/hw/char/stm32l4x5_usart.c
+++ b/hw/char/stm32l4x5_usart.c
@@ -56,7 +56,7 @@ REG32(CR1, 0x00)
FIELD(CR1, UE, 0, 1) /* USART enable */
REG32(CR2, 0x04)
FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */
- FIELD(CR2, ADD_0, 24, 1) /* ADD[3:0] */
+ FIELD(CR2, ADD_0, 24, 4) /* ADD[3:0] */
FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */
FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */
FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */
diff --git a/hw/core/Kconfig b/hw/core/Kconfig
index 9397503656..24411f5930 100644
--- a/hw/core/Kconfig
+++ b/hw/core/Kconfig
@@ -4,8 +4,14 @@ config EMPTY_SLOT
config PTIMER
bool
+config DEVICE_TREE
+ bool
+ # fail the build if libfdt not found
+ depends on FDT
+
config FITLOADER
bool
+ depends on DEVICE_TREE
config GENERIC_LOADER
bool
@@ -14,13 +20,14 @@ config GENERIC_LOADER
config GUEST_LOADER
bool
default y
- depends on TCG
+ depends on TCG && DEVICE_TREE
config OR_IRQ
bool
config PLATFORM_BUS
bool
+ depends on DEVICE_TREE
config REGISTER
bool
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index a72d48d9e1..0f0a247f56 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -30,7 +30,9 @@
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "trace.h"
+#ifdef CONFIG_PLUGIN
#include "qemu/plugin.h"
+#endif
CPUState *cpu_by_arch_id(int64_t id)
{
@@ -236,9 +238,11 @@ static void cpu_common_unrealizefn(DeviceState *dev)
CPUState *cpu = CPU(dev);
/* Call the plugin hook before clearing the cpu is fully unrealized */
+#ifdef CONFIG_PLUGIN
if (tcg_enabled()) {
qemu_plugin_vcpu_exit_hook(cpu);
}
+#endif
/* NOTE: latest generic point before the cpu is fully unrealized */
cpu_exec_unrealizefn(cpu);
diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
index 2b93fa99c9..5d8d7edcbd 100644
--- a/hw/core/machine-smp.c
+++ b/hw/core/machine-smp.c
@@ -118,76 +118,46 @@ void machine_parse_smp_config(MachineState *ms,
}
/*
- * If not supported by the machine, a topology parameter must be
- * omitted.
+ * If not supported by the machine, a topology parameter must
+ * not be set to a value greater than 1.
*/
- if (!mc->smp_props.modules_supported && config->has_modules) {
- if (config->modules > 1) {
- error_setg(errp, "modules not supported by this "
- "machine's CPU topology");
- return;
- } else {
- /* Here modules only equals 1 since we've checked zero case. */
- warn_report("Deprecated CPU topology (considered invalid): "
- "Unsupported modules parameter mustn't be "
- "specified as 1");
- }
+ if (!mc->smp_props.modules_supported &&
+ config->has_modules && config->modules > 1) {
+ error_setg(errp,
+ "modules > 1 not supported by this machine's CPU topology");
+ return;
}
modules = modules > 0 ? modules : 1;
- if (!mc->smp_props.clusters_supported && config->has_clusters) {
- if (config->clusters > 1) {
- error_setg(errp, "clusters not supported by this "
- "machine's CPU topology");
- return;
- } else {
- /* Here clusters only equals 1 since we've checked zero case. */
- warn_report("Deprecated CPU topology (considered invalid): "
- "Unsupported clusters parameter mustn't be "
- "specified as 1");
- }
+ if (!mc->smp_props.clusters_supported &&
+ config->has_clusters && config->clusters > 1) {
+ error_setg(errp,
+ "clusters > 1 not supported by this machine's CPU topology");
+ return;
}
clusters = clusters > 0 ? clusters : 1;
- if (!mc->smp_props.dies_supported && config->has_dies) {
- if (config->dies > 1) {
- error_setg(errp, "dies not supported by this "
- "machine's CPU topology");
- return;
- } else {
- /* Here dies only equals 1 since we've checked zero case. */
- warn_report("Deprecated CPU topology (considered invalid): "
- "Unsupported dies parameter mustn't be "
- "specified as 1");
- }
+ if (!mc->smp_props.dies_supported &&
+ config->has_dies && config->dies > 1) {
+ error_setg(errp,
+ "dies > 1 not supported by this machine's CPU topology");
+ return;
}
dies = dies > 0 ? dies : 1;
- if (!mc->smp_props.books_supported && config->has_books) {
- if (config->books > 1) {
- error_setg(errp, "books not supported by this "
- "machine's CPU topology");
- return;
- } else {
- /* Here books only equals 1 since we've checked zero case. */
- warn_report("Deprecated CPU topology (considered invalid): "
- "Unsupported books parameter mustn't be "
- "specified as 1");
- }
+ if (!mc->smp_props.books_supported &&
+ config->has_books && config->books > 1) {
+ error_setg(errp,
+ "books > 1 not supported by this machine's CPU topology");
+ return;
}
books = books > 0 ? books : 1;
- if (!mc->smp_props.drawers_supported && config->has_drawers) {
- if (config->drawers > 1) {
- error_setg(errp, "drawers not supported by this "
- "machine's CPU topology");
- return;
- } else {
- /* Here drawers only equals 1 since we've checked zero case. */
- warn_report("Deprecated CPU topology (considered invalid): "
- "Unsupported drawers parameter mustn't be "
- "specified as 1");
- }
+ if (!mc->smp_props.drawers_supported &&
+ config->has_drawers && config->drawers > 1) {
+ error_setg(errp,
+ "drawers > 1 not supported by this machine's CPU topology");
+ return;
}
drawers = drawers > 0 ? drawers : 1;
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 4ff60911e7..8087026b45 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -35,6 +35,7 @@
GlobalProperty hw_compat_9_0[] = {
{"arm-cpu", "backcompat-cntfrq", "true" },
+ {"vfio-pci", "skip-vsc-check", "false" },
};
const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0);
@@ -42,6 +43,7 @@ GlobalProperty hw_compat_8_2[] = {
{ "migration", "zero-page-detection", "legacy"},
{ TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" },
{ TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "64" },
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
};
const size_t hw_compat_8_2_len = G_N_ELEMENTS(hw_compat_8_2);
@@ -50,15 +52,15 @@ GlobalProperty hw_compat_8_1[] = {
{ "ramfb", "x-migrate", "off" },
{ "vfio-pci-nohotplug", "x-ramfb-migrate", "off" },
{ "igb", "x-pcie-flr-init", "off" },
+ { TYPE_VIRTIO_NET, "host_uso", "off"},
+ { TYPE_VIRTIO_NET, "guest_uso4", "off"},
+ { TYPE_VIRTIO_NET, "guest_uso6", "off"},
};
const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1);
GlobalProperty hw_compat_8_0[] = {
{ "migration", "multifd-flush-after-each-section", "on"},
{ TYPE_PCI_DEVICE, "x-pcie-ari-nextfn-1", "on" },
- { TYPE_VIRTIO_NET, "host_uso", "off"},
- { TYPE_VIRTIO_NET, "guest_uso4", "off"},
- { TYPE_VIRTIO_NET, "guest_uso6", "off"},
};
const size_t hw_compat_8_0_len = G_N_ELEMENTS(hw_compat_8_0);
@@ -192,7 +194,6 @@ GlobalProperty hw_compat_3_0[] = {};
const size_t hw_compat_3_0_len = G_N_ELEMENTS(hw_compat_3_0);
GlobalProperty hw_compat_2_12[] = {
- { "migration", "decompress-error-check", "off" },
{ "hda-audio", "use-timer", "false" },
{ "cirrus-vga", "global-vmstate", "true" },
{ "VGA", "global-vmstate", "true" },
diff --git a/hw/core/meson.build b/hw/core/meson.build
index f20d4143f7..a3d9bab9f4 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -16,7 +16,7 @@ common_ss.add(files('cpu-common.c'))
common_ss.add(files('machine-smp.c'))
system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
-system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
+system_ss.add(when: 'CONFIG_GUEST_LOADER', if_true: files('guest-loader.c'))
system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))
system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c'))
system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))
diff --git a/hw/cris/Kconfig b/hw/cris/Kconfig
index 884ad2cbc0..26c7eef743 100644
--- a/hw/cris/Kconfig
+++ b/hw/cris/Kconfig
@@ -1,5 +1,7 @@
config AXIS
bool
+ default y
+ depends on CRIS
select ETRAXFS
select PFLASH_CFI02
select NAND
diff --git a/hw/display/meson.build b/hw/display/meson.build
index 7893b94c8e..7db05eace9 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -125,12 +125,14 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA')
if_false: files('acpi-vga-stub.c'))
hw_display_modules += {'virtio-vga': virtio_vga_ss}
- virtio_vga_gl_ss = ss.source_set()
- virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl],
- if_true: [files('virtio-vga-gl.c'), pixman])
- virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
- if_false: files('acpi-vga-stub.c'))
- hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss}
+ if virgl.found() and opengl.found()
+ virtio_vga_gl_ss = ss.source_set()
+ virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl],
+ if_true: [files('virtio-vga-gl.c'), pixman])
+ virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
+ if_false: files('acpi-vga-stub.c'))
+ hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss}
+ endif
if rutabaga.found()
virtio_vga_rutabaga_ss = ss.source_set()
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 876a1d3697..f77c1c1145 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -25,6 +25,7 @@
#ifndef HW_VGA_INT_H
#define HW_VGA_INT_H
+#include "ui/console.h"
#include "exec/ioport.h"
#include "exec/memory.h"
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index 709c8a02a1..e4b398d26c 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -249,6 +249,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
case VHOST_USER_GPU_DMABUF_SCANOUT: {
VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout;
int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr);
+ uint64_t modifier = 0;
QemuDmaBuf *dmabuf;
if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
@@ -261,30 +262,33 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
g->parent_obj.enable = 1;
con = g->parent_obj.scanout[m->scanout_id].con;
- dmabuf = &g->dmabuf[m->scanout_id];
- if (dmabuf->fd >= 0) {
- close(dmabuf->fd);
- dmabuf->fd = -1;
+ dmabuf = g->dmabuf[m->scanout_id];
+
+ if (dmabuf) {
+ qemu_dmabuf_close(dmabuf);
+ dpy_gl_release_dmabuf(con, dmabuf);
+ g_clear_pointer(&dmabuf, qemu_dmabuf_free);
}
- dpy_gl_release_dmabuf(con, dmabuf);
+
if (fd == -1) {
dpy_gl_scanout_disable(con);
+ g->dmabuf[m->scanout_id] = NULL;
break;
}
- *dmabuf = (QemuDmaBuf) {
- .fd = fd,
- .width = m->fd_width,
- .height = m->fd_height,
- .stride = m->fd_stride,
- .fourcc = m->fd_drm_fourcc,
- .y0_top = m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
- };
+
if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) {
VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2;
- dmabuf->modifier = m2->modifier;
+ modifier = m2->modifier;
}
+ dmabuf = qemu_dmabuf_new(m->fd_width, m->fd_height,
+ m->fd_stride, 0, 0, 0, 0,
+ m->fd_drm_fourcc, modifier,
+ fd, false, m->fd_flags &
+ VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP);
+
dpy_gl_scanout_dmabuf(con, dmabuf);
+ g->dmabuf[m->scanout_id] = dmabuf;
break;
}
case VHOST_USER_GPU_DMABUF_UPDATE: {
diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c
index d51184d658..c02ec6d37d 100644
--- a/hw/display/virtio-gpu-udmabuf.c
+++ b/hw/display/virtio-gpu-udmabuf.c
@@ -162,7 +162,8 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
struct virtio_gpu_scanout *scanout;
scanout = &g->parent_obj.scanout[dmabuf->scanout_id];
- dpy_gl_release_dmabuf(scanout->con, &dmabuf->buf);
+ dpy_gl_release_dmabuf(scanout->con, dmabuf->buf);
+ g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
g_free(dmabuf);
}
@@ -181,17 +182,10 @@ static VGPUDMABuf
}
dmabuf = g_new0(VGPUDMABuf, 1);
- dmabuf->buf.width = r->width;
- dmabuf->buf.height = r->height;
- dmabuf->buf.stride = fb->stride;
- dmabuf->buf.x = r->x;
- dmabuf->buf.y = r->y;
- dmabuf->buf.backing_width = fb->width;
- dmabuf->buf.backing_height = fb->height;
- dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format);
- dmabuf->buf.fd = res->dmabuf_fd;
- dmabuf->buf.allow_fences = true;
- dmabuf->buf.draw_submitted = false;
+ dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride,
+ r->x, r->y, fb->width, fb->height,
+ qemu_pixman_to_drm_format(fb->format),
+ 0, res->dmabuf_fd, true, false);
dmabuf->scanout_id = scanout_id;
QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next);
@@ -206,6 +200,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
{
struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
VGPUDMABuf *new_primary, *old_primary = NULL;
+ uint32_t width, height;
new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r);
if (!new_primary) {
@@ -216,11 +211,11 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
old_primary = g->dmabuf.primary[scanout_id];
}
+ width = qemu_dmabuf_get_width(new_primary->buf);
+ height = qemu_dmabuf_get_height(new_primary->buf);
g->dmabuf.primary[scanout_id] = new_primary;
- qemu_console_resize(scanout->con,
- new_primary->buf.width,
- new_primary->buf.height);
- dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf);
+ qemu_console_resize(scanout->con, width, height);
+ dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf);
if (old_primary) {
virtio_gpu_free_dmabuf(g, old_primary);
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index ae831b6b3e..d60b1b2973 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1166,10 +1166,17 @@ static void virtio_gpu_cursor_bh(void *opaque)
virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq);
}
+static bool scanout_vmstate_after_v2(void *opaque, int version)
+{
+ struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout);
+ struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj);
+
+ return gpu->scanout_vmstate_version >= 2;
+}
+
static const VMStateDescription vmstate_virtio_gpu_scanout = {
.name = "virtio-gpu-one-scanout",
- .version_id = 2,
- .minimum_version_id = 1,
+ .version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout),
VMSTATE_UINT32(width, struct virtio_gpu_scanout),
@@ -1181,12 +1188,18 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = {
VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout),
- VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
VMSTATE_END_OF_LIST()
},
};
@@ -1659,6 +1672,7 @@ static Property virtio_gpu_properties[] = {
DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0),
+ DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index b2130a0d70..27536bfce0 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -972,7 +972,7 @@ static void fb_event(struct XenLegacyDevice *xendev)
/* -------------------------------------------------------------------- */
-struct XenDevOps xen_kbdmouse_ops = {
+static struct XenDevOps xen_kbdmouse_ops = {
.size = sizeof(struct XenInput),
.init = input_init,
.initialise = input_initialise,
@@ -995,3 +995,9 @@ static const GraphicHwOps xenfb_ops = {
.gfx_update = xenfb_update,
.ui_info = xenfb_ui_info,
};
+
+static void xen_vkbd_register_backend(void)
+{
+ xen_be_register("vkbd", &xen_kbdmouse_ops);
+}
+xen_backend_init(xen_vkbd_register_backend);
diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c
index 530717d188..dde4aeca40 100644
--- a/hw/dma/xlnx_dpdma.c
+++ b/hw/dma/xlnx_dpdma.c
@@ -614,6 +614,65 @@ static void xlnx_dpdma_register_types(void)
type_register_static(&xlnx_dpdma_info);
}
+static MemTxResult xlnx_dpdma_read_descriptor(XlnxDPDMAState *s,
+ uint64_t desc_addr,
+ DPDMADescriptor *desc)
+{
+ MemTxResult res = dma_memory_read(&address_space_memory, desc_addr,
+ &desc, sizeof(DPDMADescriptor),
+ MEMTXATTRS_UNSPECIFIED);
+ if (res) {
+ return res;
+ }
+
+ /* Convert from LE into host endianness. */
+ desc->control = le32_to_cpu(desc->control);
+ desc->descriptor_id = le32_to_cpu(desc->descriptor_id);
+ desc->xfer_size = le32_to_cpu(desc->xfer_size);
+ desc->line_size_stride = le32_to_cpu(desc->line_size_stride);
+ desc->timestamp_lsb = le32_to_cpu(desc->timestamp_lsb);
+ desc->timestamp_msb = le32_to_cpu(desc->timestamp_msb);
+ desc->address_extension = le32_to_cpu(desc->address_extension);
+ desc->next_descriptor = le32_to_cpu(desc->next_descriptor);
+ desc->source_address = le32_to_cpu(desc->source_address);
+ desc->address_extension_23 = le32_to_cpu(desc->address_extension_23);
+ desc->address_extension_45 = le32_to_cpu(desc->address_extension_45);
+ desc->source_address2 = le32_to_cpu(desc->source_address2);
+ desc->source_address3 = le32_to_cpu(desc->source_address3);
+ desc->source_address4 = le32_to_cpu(desc->source_address4);
+ desc->source_address5 = le32_to_cpu(desc->source_address5);
+ desc->crc = le32_to_cpu(desc->crc);
+
+ return res;
+}
+
+static MemTxResult xlnx_dpdma_write_descriptor(uint64_t desc_addr,
+ DPDMADescriptor *desc)
+{
+ DPDMADescriptor tmp_desc = *desc;
+
+ /* Convert from host endianness into LE. */
+ tmp_desc.control = cpu_to_le32(tmp_desc.control);
+ tmp_desc.descriptor_id = cpu_to_le32(tmp_desc.descriptor_id);
+ tmp_desc.xfer_size = cpu_to_le32(tmp_desc.xfer_size);
+ tmp_desc.line_size_stride = cpu_to_le32(tmp_desc.line_size_stride);
+ tmp_desc.timestamp_lsb = cpu_to_le32(tmp_desc.timestamp_lsb);
+ tmp_desc.timestamp_msb = cpu_to_le32(tmp_desc.timestamp_msb);
+ tmp_desc.address_extension = cpu_to_le32(tmp_desc.address_extension);
+ tmp_desc.next_descriptor = cpu_to_le32(tmp_desc.next_descriptor);
+ tmp_desc.source_address = cpu_to_le32(tmp_desc.source_address);
+ tmp_desc.address_extension_23 = cpu_to_le32(tmp_desc.address_extension_23);
+ tmp_desc.address_extension_45 = cpu_to_le32(tmp_desc.address_extension_45);
+ tmp_desc.source_address2 = cpu_to_le32(tmp_desc.source_address2);
+ tmp_desc.source_address3 = cpu_to_le32(tmp_desc.source_address3);
+ tmp_desc.source_address4 = cpu_to_le32(tmp_desc.source_address4);
+ tmp_desc.source_address5 = cpu_to_le32(tmp_desc.source_address5);
+ tmp_desc.crc = cpu_to_le32(tmp_desc.crc);
+
+ return dma_memory_write(&address_space_memory, desc_addr, &tmp_desc,
+ sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED);
+}
+
size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
bool one_desc)
{
@@ -651,8 +710,7 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
desc_addr = xlnx_dpdma_descriptor_next_address(s, channel);
}
- if (dma_memory_read(&address_space_memory, desc_addr, &desc,
- sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED)) {
+ if (xlnx_dpdma_read_descriptor(s, desc_addr, &desc)) {
s->registers[DPDMA_EISR] |= ((1 << 1) << channel);
xlnx_dpdma_update_irq(s);
s->operation_finished[channel] = true;
@@ -755,8 +813,10 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
/* The descriptor need to be updated when it's completed. */
DPRINTF("update the descriptor with the done flag set.\n");
xlnx_dpdma_desc_set_done(&desc);
- dma_memory_write(&address_space_memory, desc_addr, &desc,
- sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED);
+ if (xlnx_dpdma_write_descriptor(desc_addr, &desc)) {
+ DPRINTF("Can't write the descriptor.\n");
+ /* TODO: check hardware behaviour for memory write failure */
+ }
}
if (xlnx_dpdma_desc_completion_interrupt(&desc)) {
diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c
index 71bf5fddb2..30d8d6cba4 100644
--- a/hw/gpio/stm32l4x5_gpio.c
+++ b/hw/gpio/stm32l4x5_gpio.c
@@ -20,6 +20,7 @@
#include "qemu/log.h"
#include "hw/gpio/stm32l4x5_gpio.h"
#include "hw/irq.h"
+#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "qapi/visitor.h"
@@ -426,8 +427,8 @@ static void stm32l4x5_gpio_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_stm32l4x5_gpio = {
.name = TYPE_STM32L4X5_GPIO,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]){
VMSTATE_UINT32(moder, Stm32l4x5GpioState),
VMSTATE_UINT32(otyper, Stm32l4x5GpioState),
@@ -441,6 +442,7 @@ static const VMStateDescription vmstate_stm32l4x5_gpio = {
VMSTATE_UINT32(ascr, Stm32l4x5GpioState),
VMSTATE_UINT16(disconnected_pins, Stm32l4x5GpioState),
VMSTATE_UINT16(pins_connected_high, Stm32l4x5GpioState),
+ VMSTATE_CLOCK(clk, Stm32l4x5GpioState),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c
index 5884804c58..7342440b95 100644
--- a/hw/gpio/zaurus.c
+++ b/hw/gpio/zaurus.c
@@ -49,19 +49,20 @@ struct ScoopInfo {
uint16_t isr;
};
-#define SCOOP_MCR 0x00
-#define SCOOP_CDR 0x04
-#define SCOOP_CSR 0x08
-#define SCOOP_CPR 0x0c
-#define SCOOP_CCR 0x10
-#define SCOOP_IRR_IRM 0x14
-#define SCOOP_IMR 0x18
-#define SCOOP_ISR 0x1c
-#define SCOOP_GPCR 0x20
-#define SCOOP_GPWR 0x24
-#define SCOOP_GPRR 0x28
-
-static inline void scoop_gpio_handler_update(ScoopInfo *s) {
+#define SCOOP_MCR 0x00
+#define SCOOP_CDR 0x04
+#define SCOOP_CSR 0x08
+#define SCOOP_CPR 0x0c
+#define SCOOP_CCR 0x10
+#define SCOOP_IRR_IRM 0x14
+#define SCOOP_IMR 0x18
+#define SCOOP_ISR 0x1c
+#define SCOOP_GPCR 0x20
+#define SCOOP_GPWR 0x24
+#define SCOOP_GPRR 0x28
+
+static inline void scoop_gpio_handler_update(ScoopInfo *s)
+{
uint32_t level, diff;
int bit;
level = s->gpio_level & s->gpio_dir;
@@ -125,8 +126,9 @@ static void scoop_write(void *opaque, hwaddr addr,
break;
case SCOOP_CPR:
s->power = value;
- if (value & 0x80)
+ if (value & 0x80) {
s->power |= 0x8040;
+ }
break;
case SCOOP_CCR:
s->ccr = value;
@@ -145,7 +147,7 @@ static void scoop_write(void *opaque, hwaddr addr,
scoop_gpio_handler_update(s);
break;
case SCOOP_GPWR:
- case SCOOP_GPRR: /* GPRR is probably R/O in real HW */
+ case SCOOP_GPRR: /* GPRR is probably R/O in real HW */
s->gpio_level = value & s->gpio_dir;
scoop_gpio_handler_update(s);
break;
@@ -166,10 +168,11 @@ static void scoop_gpio_set(void *opaque, int line, int level)
{
ScoopInfo *s = (ScoopInfo *) opaque;
- if (level)
+ if (level) {
s->gpio_level |= (1 << line);
- else
+ } else {
s->gpio_level &= ~(1 << line);
+ }
}
static void scoop_init(Object *obj)
@@ -203,7 +206,7 @@ static int scoop_post_load(void *opaque, int version_id)
return 0;
}
-static bool is_version_0 (void *opaque, int version_id)
+static bool is_version_0(void *opaque, int version_id)
{
return version_id == 0;
}
@@ -265,7 +268,7 @@ type_init(scoop_register_types)
/* Write the bootloader parameters memory area. */
-#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a)
+#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a)
static struct QEMU_PACKED sl_param_info {
uint32_t comadj_keyword;
@@ -286,16 +289,16 @@ static struct QEMU_PACKED sl_param_info {
uint32_t phad_keyword;
int32_t phadadj;
} zaurus_bootparam = {
- .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'),
- .comadj = 125,
- .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'),
- .uuid = { -1 },
- .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'),
- .touch_xp = -1,
- .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'),
- .adadj = -1,
- .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'),
- .phadadj = 0x01,
+ .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'),
+ .comadj = 125,
+ .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'),
+ .uuid = { -1 },
+ .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'),
+ .touch_xp = -1,
+ .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'),
+ .adadj = -1,
+ .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'),
+ .phadadj = 0x01,
};
void sl_bootparam_write(hwaddr ptr)
diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig
index ee7ffd2bfb..d4d457f4ab 100644
--- a/hw/hppa/Kconfig
+++ b/hw/hppa/Kconfig
@@ -1,5 +1,7 @@
config HPPA_B160L
bool
+ default y
+ depends on HPPA
imply PCI_DEVICES
imply E1000_PCI
imply USB_OHCI_PCI
diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c
index 37ee6387e0..5d0a8739de 100644
--- a/hw/hppa/machine.c
+++ b/hw/hppa/machine.c
@@ -207,37 +207,37 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus,
val = cpu_to_le64(MIN_SEABIOS_HPPA_VERSION);
fw_cfg_add_file(fw_cfg, "/etc/firmware-min-version",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
val = cpu_to_le64(HPPA_TLB_ENTRIES - btlb_entries);
fw_cfg_add_file(fw_cfg, "/etc/cpu/tlb_entries",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
val = cpu_to_le64(btlb_entries);
fw_cfg_add_file(fw_cfg, "/etc/cpu/btlb_entries",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
len = strlen(mc->name) + 1;
fw_cfg_add_file(fw_cfg, "/etc/hppa/machine",
- g_memdup(mc->name, len), len);
+ g_memdup2(mc->name, len), len);
val = cpu_to_le64(soft_power_reg);
fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
val = cpu_to_le64(CPU_HPA + 16);
fw_cfg_add_file(fw_cfg, "/etc/hppa/rtc-addr",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
val = cpu_to_le64(CPU_HPA + 24);
fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort",
- g_memdup(&val, sizeof(val)), sizeof(val));
+ g_memdup2(&val, sizeof(val)), sizeof(val));
fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]);
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
fw_cfg_add_file(fw_cfg, "/etc/qemu-version",
- g_memdup(qemu_version, sizeof(qemu_version)),
+ g_memdup2(qemu_version, sizeof(qemu_version)),
sizeof(qemu_version));
fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg);
diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c
index 3ea54ba818..483dcca308 100644
--- a/hw/hyperv/hyperv.c
+++ b/hw/hyperv/hyperv.c
@@ -373,6 +373,31 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno)
return ret;
}
+static int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint)
+{
+ struct kvm_irq_routing_entry kroute = {};
+ int virq;
+
+ if (!kvm_gsi_routing_enabled()) {
+ return -ENOSYS;
+ }
+ virq = kvm_irqchip_get_virq(s);
+ if (virq < 0) {
+ return virq;
+ }
+
+ kroute.gsi = virq;
+ kroute.type = KVM_IRQ_ROUTING_HV_SINT;
+ kroute.flags = 0;
+ kroute.u.hv_sint.vcpu = vcpu;
+ kroute.u.hv_sint.sint = sint;
+
+ kvm_add_routing_entry(s, &kroute);
+ kvm_irqchip_commit_routes(s);
+
+ return virq;
+}
+
HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
HvSintMsgCb cb, void *cb_data)
{
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index a6ee052f9a..f4a33b6c08 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -32,7 +32,7 @@ config PC
imply VGA_PCI
imply VIRTIO_VGA
imply NVDIMM
- select FDC_ISA
+ imply FDC_ISA
select I8259
select I8254
select PCKBD
@@ -66,6 +66,8 @@ config PC_ACPI
config I440FX
bool
+ default y
+ depends on I386
imply E1000_PCI
imply VMPORT
imply VMMOUSE
@@ -81,6 +83,8 @@ config I440FX
config ISAPC
bool
+ default y
+ depends on I386
imply VGA_ISA
select ISA_BUS
select PC
@@ -91,6 +95,8 @@ config ISAPC
config Q35
bool
+ default y
+ depends on I386
imply VTD
imply AMD_IOMMU
imply E1000E_PCI_EXPRESS
@@ -108,6 +114,9 @@ config Q35
config MICROVM
bool
+ default y
+ depends on I386 && FDT
+ select DEVICE_TREE
select SERIAL_ISA # for serial_hds_isa_init()
select ISA_BUS
select APIC
@@ -142,4 +151,4 @@ config VMMOUSE
config XEN_EMU
bool
default y
- depends on KVM && (I386 || X86_64)
+ depends on KVM && I386
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index d802d2787f..6e0d9945d0 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -203,6 +203,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg)
fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val));
}
+#ifdef CONFIG_ACPI
void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg)
{
/*
@@ -229,3 +230,4 @@ void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg)
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
+#endif
diff --git a/hw/i386/meson.build b/hw/i386/meson.build
index d8b70ef3e9..03aad10df7 100644
--- a/hw/i386/meson.build
+++ b/hw/i386/meson.build
@@ -1,18 +1,20 @@
i386_ss = ss.source_set()
i386_ss.add(files(
'fw_cfg.c',
- 'vapic.c',
'e820_memory_layout.c',
+ 'monitor.c',
'multiboot.c',
'x86.c',
+ 'x86-cpu.c',
))
+i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c'))
i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'),
if_false: files('x86-iommu-stub.c'))
i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'),
if_false: files('amd_iommu-stub.c'))
i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c'))
-i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c', 'microvm-dt.c'))
+i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c'))
i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c'))
i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c'))
i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c'))
@@ -22,6 +24,7 @@ i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'),
i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c'))
i386_ss.add(when: 'CONFIG_PC', if_true: files(
+ 'x86-common.c',
'pc.c',
'pc_sysfw.c',
'acpi-build.c',
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
index 61a772dfe6..fec63cacfa 100644
--- a/hw/i386/microvm.c
+++ b/hw/i386/microvm.c
@@ -278,7 +278,7 @@ static void microvm_devices_init(MicrovmMachineState *mms)
default_firmware = x86_machine_is_acpi_enabled(x86ms)
? MICROVM_BIOS_FILENAME
: MICROVM_QBOOT_FILENAME;
- x86_bios_rom_init(MACHINE(mms), default_firmware, get_system_memory(), true);
+ x86_bios_rom_init(x86ms, default_firmware, get_system_memory(), true);
}
static void microvm_memory_init(MicrovmMachineState *mms)
diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c
new file mode 100644
index 0000000000..1ebd3564bf
--- /dev/null
+++ b/hw/i386/monitor.c
@@ -0,0 +1,46 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * 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 "qemu/osdep.h"
+#include "monitor/monitor.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-misc-target.h"
+#include "hw/i386/x86.h"
+#include "hw/rtc/mc146818rtc.h"
+
+#include CONFIG_DEVICES
+
+void qmp_rtc_reset_reinjection(Error **errp)
+{
+ X86MachineState *x86ms = X86_MACHINE(qdev_get_machine());
+
+#ifdef CONFIG_MC146818RTC
+ if (x86ms->rtc) {
+ rtc_reset_reinjection(MC146818_RTC(x86ms->rtc));
+ }
+#else
+ assert(!x86ms->rtc);
+#endif
+}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 08c7de416f..bfb46e9b54 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -81,6 +81,7 @@
GlobalProperty pc_compat_9_0[] = {
{ TYPE_X86_CPU, "guest-phys-bits", "0" },
{ "sev-guest", "legacy-vm-type", "true" },
+ { TYPE_X86_CPU, "legacy-multi-node", "on" },
};
const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0);
@@ -439,16 +440,19 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp)
static void pc_cmos_init_floppy(MC146818RtcState *rtc_state, ISADevice *floppy)
{
- int val, nb, i;
+ int val, nb;
FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE,
FLOPPY_DRIVE_TYPE_NONE };
+#ifdef CONFIG_FDC_ISA
/* floppy type */
if (floppy) {
- for (i = 0; i < 2; i++) {
+ for (int i = 0; i < 2; i++) {
fd_type[i] = isa_fdc_get_drive_type(floppy, i);
}
}
+#endif
+
val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
cmos_get_fd_drive_type(fd_type[1]);
mc146818rtc_set_cmos_data(rtc_state, 0x10, val);
@@ -1132,7 +1136,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl,
int i;
DriveInfo *fd[MAX_FD];
qemu_irq *a20_line;
- ISADevice *fdc, *i8042, *port92, *vmmouse;
+ ISADevice *i8042, *port92, *vmmouse;
serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
@@ -1142,11 +1146,13 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl,
create_fdctrl |= !!fd[i];
}
if (create_fdctrl) {
- fdc = isa_new(TYPE_ISA_FDC);
+#ifdef CONFIG_FDC_ISA
+ ISADevice *fdc = isa_new(TYPE_ISA_FDC);
if (fdc) {
isa_realize_and_unref(fdc, isa_bus, &error_fatal);
isa_fdc_init_drives(fdc, fd);
}
+#endif
}
if (!create_i8042) {
@@ -1244,7 +1250,6 @@ void pc_basic_device_init(struct PCMachineState *pcms,
pci_create_simple(pcms->pcibus, -1, "xen-platform");
}
xen_bus_init();
- xen_be_init();
}
#endif
@@ -1821,9 +1826,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = pc_get_hotplug_handler;
mc->hotplug_allowed = pc_hotplug_allowed;
- mc->cpu_index_to_instance_props = x86_cpu_index_to_props;
- mc->get_default_cpu_node_id = x86_get_default_cpu_node_id;
- mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
mc->has_hotpluggable_cpus = true;
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 8850c49c66..99efb3c45c 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -317,8 +317,8 @@ static void pc_init1(MachineState *machine, const char *pci_type)
}
/* init basic PC hardware */
- pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, true,
- 0x4);
+ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc,
+ !MACHINE_CLASS(pcmc)->no_floppy, 0x4);
pc_nic_init(pcmc, isa_bus, pcms->pcibus);
@@ -501,6 +501,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
m->default_machine_opts = "firmware=bios-256k.bin";
m->default_display = "std";
m->default_nic = "e1000";
+ m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC);
m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL);
machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE);
machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
@@ -931,6 +932,7 @@ static void isapc_machine_options(MachineClass *m)
pcmc->has_reserved_memory = false;
m->default_nic = "ne2k_isa";
m->default_cpu_type = X86_CPU_TYPE_NAME("486");
+ m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC);
m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL);
}
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 87b5bf59d6..82d37cb376 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -40,11 +40,10 @@
#define FLASH_SECTOR_SIZE 4096
-static void pc_isa_bios_init(MemoryRegion *rom_memory,
+static void pc_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *rom_memory,
MemoryRegion *flash_mem)
{
int isa_bios_size;
- MemoryRegion *isa_bios;
uint64_t flash_size;
void *flash_ptr, *isa_bios_ptr;
@@ -52,7 +51,6 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
/* map the last 128KB of the BIOS in ISA space */
isa_bios_size = MIN(flash_size, 128 * KiB);
- isa_bios = g_malloc(sizeof(*isa_bios));
memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size,
&error_fatal);
memory_region_add_subregion_overlap(rom_memory,
@@ -136,6 +134,7 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms)
static void pc_system_flash_map(PCMachineState *pcms,
MemoryRegion *rom_memory)
{
+ X86MachineState *x86ms = X86_MACHINE(pcms);
hwaddr total_size = 0;
int i;
BlockBackend *blk;
@@ -185,7 +184,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
if (i == 0) {
flash_mem = pflash_cfi01_get_memory(system_flash);
- pc_isa_bios_init(rom_memory, flash_mem);
+ pc_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem);
/* Encrypt the pflash boot ROM */
if (sev_enabled()) {
@@ -205,7 +204,7 @@ void pc_system_firmware_init(PCMachineState *pcms,
BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)];
if (!pcmc->pci_enabled) {
- x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, true);
+ x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true);
return;
}
@@ -226,7 +225,7 @@ void pc_system_firmware_init(PCMachineState *pcms,
if (!pflash_blk[0]) {
/* Machine property pflash0 not set, use ROM mode */
- x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, false);
+ x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false);
} else {
if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
/*
diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c
new file mode 100644
index 0000000000..67b03c913a
--- /dev/null
+++ b/hw/i386/x86-common.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2019, 2024 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 "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/cutils.h"
+#include "qemu/units.h"
+#include "qemu/datadir.h"
+#include "qapi/error.h"
+#include "sysemu/numa.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/xen.h"
+#include "trace.h"
+
+#include "hw/i386/x86.h"
+#include "target/i386/cpu.h"
+#include "hw/rtc/mc146818rtc.h"
+#include "target/i386/sev.h"
+
+#include "hw/acpi/cpu_hotplug.h"
+#include "hw/irq.h"
+#include "hw/loader.h"
+#include "multiboot.h"
+#include "elf.h"
+#include "standard-headers/asm-x86/bootparam.h"
+#include CONFIG_DEVICES
+#include "kvm/kvm_i386.h"
+
+#ifdef CONFIG_XEN_EMU
+#include "hw/xen/xen.h"
+#include "hw/i386/kvm/xen_evtchn.h"
+#endif
+
+/* Physical Address of PVH entry point read from kernel ELF NOTE */
+static size_t pvh_start_addr;
+
+static void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp)
+{
+ Object *cpu = object_new(MACHINE(x86ms)->cpu_type);
+
+ if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) {
+ goto out;
+ }
+ qdev_realize(DEVICE(cpu), NULL, errp);
+
+out:
+ object_unref(cpu);
+}
+
+void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
+{
+ int i;
+ const CPUArchIdList *possible_cpus;
+ MachineState *ms = MACHINE(x86ms);
+ MachineClass *mc = MACHINE_GET_CLASS(x86ms);
+
+ x86_cpu_set_default_version(default_cpu_version);
+
+ /*
+ * Calculates the limit to CPU APIC ID values
+ *
+ * Limit for the APIC ID value, so that all
+ * CPU APIC IDs are < x86ms->apic_id_limit.
+ *
+ * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create().
+ */
+ x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms,
+ ms->smp.max_cpus - 1) + 1;
+
+ /*
+ * Can we support APIC ID 255 or higher? With KVM, that requires
+ * both in-kernel lapic and X2APIC userspace API.
+ *
+ * kvm_enabled() must go first to ensure that kvm_* references are
+ * not emitted for the linker to consume (kvm_enabled() is
+ * a literal `0` in configurations where kvm_* aren't defined)
+ */
+ if (kvm_enabled() && x86ms->apic_id_limit > 255 &&
+ kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) {
+ error_report("current -smp configuration requires kernel "
+ "irqchip and X2APIC API support.");
+ exit(EXIT_FAILURE);
+ }
+
+ if (kvm_enabled()) {
+ kvm_set_max_apic_id(x86ms->apic_id_limit);
+ }
+
+ if (!kvm_irqchip_in_kernel()) {
+ apic_set_max_apic_id(x86ms->apic_id_limit);
+ }
+
+ possible_cpus = mc->possible_cpu_arch_ids(ms);
+ for (i = 0; i < ms->smp.cpus; i++) {
+ x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
+ }
+}
+
+void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count)
+{
+ MC146818RtcState *rtc = MC146818_RTC(s);
+
+ if (cpus_count > 0xff) {
+ /*
+ * If the number of CPUs can't be represented in 8 bits, the
+ * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just
+ * to make old BIOSes fail more predictably.
+ */
+ mc146818rtc_set_cmos_data(rtc, 0x5f, 0);
+ } else {
+ mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1);
+ }
+}
+
+static int x86_apic_cmp(const void *a, const void *b)
+{
+ CPUArchId *apic_a = (CPUArchId *)a;
+ CPUArchId *apic_b = (CPUArchId *)b;
+
+ return apic_a->arch_id - apic_b->arch_id;
+}
+
+/*
+ * returns pointer to CPUArchId descriptor that matches CPU's apic_id
+ * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no
+ * entry corresponding to CPU's apic_id returns NULL.
+ */
+static CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
+{
+ CPUArchId apic_id, *found_cpu;
+
+ apic_id.arch_id = id;
+ found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
+ ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
+ x86_apic_cmp);
+ if (found_cpu && idx) {
+ *idx = found_cpu - ms->possible_cpus->cpus;
+ }
+ return found_cpu;
+}
+
+void x86_cpu_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *found_cpu;
+ Error *local_err = NULL;
+ X86CPU *cpu = X86_CPU(dev);
+ X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
+
+ if (x86ms->acpi_dev) {
+ hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+
+ /* increment the number of CPUs */
+ x86ms->boot_cpus++;
+ if (x86ms->rtc) {
+ x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
+ }
+ if (x86ms->fw_cfg) {
+ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
+ }
+
+ found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
+ found_cpu->cpu = CPU(dev);
+out:
+ error_propagate(errp, local_err);
+}
+
+void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ int idx = -1;
+ X86CPU *cpu = X86_CPU(dev);
+ X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
+
+ if (!x86ms->acpi_dev) {
+ error_setg(errp, "CPU hot unplug not supported without ACPI");
+ return;
+ }
+
+ x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
+ assert(idx != -1);
+ if (idx == 0) {
+ error_setg(errp, "Boot CPU is unpluggable");
+ return;
+ }
+
+ hotplug_handler_unplug_request(x86ms->acpi_dev, dev,
+ errp);
+}
+
+void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *found_cpu;
+ Error *local_err = NULL;
+ X86CPU *cpu = X86_CPU(dev);
+ X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
+
+ hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
+ found_cpu->cpu = NULL;
+ qdev_unrealize(dev);
+
+ /* decrement the number of CPUs */
+ x86ms->boot_cpus--;
+ /* Update the number of CPUs in CMOS */
+ x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
+ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
+ out:
+ error_propagate(errp, local_err);
+}
+
+void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ int idx;
+ CPUState *cs;
+ CPUArchId *cpu_slot;
+ X86CPUTopoIDs topo_ids;
+ X86CPU *cpu = X86_CPU(dev);
+ CPUX86State *env = &cpu->env;
+ MachineState *ms = MACHINE(hotplug_dev);
+ X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
+ unsigned int smp_cores = ms->smp.cores;
+ unsigned int smp_threads = ms->smp.threads;
+ X86CPUTopoInfo topo_info;
+
+ if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
+ error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
+ ms->cpu_type);
+ return;
+ }
+
+ if (x86ms->acpi_dev) {
+ Error *local_err = NULL;
+
+ hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ init_topo_info(&topo_info, x86ms);
+
+ env->nr_dies = ms->smp.dies;
+
+ /*
+ * If APIC ID is not set,
+ * set it based on socket/die/core/thread properties.
+ */
+ if (cpu->apic_id == UNASSIGNED_APIC_ID) {
+ int max_socket = (ms->smp.max_cpus - 1) /
+ smp_threads / smp_cores / ms->smp.dies;
+
+ /*
+ * die-id was optional in QEMU 4.0 and older, so keep it optional
+ * if there's only one die per socket.
+ */
+ if (cpu->die_id < 0 && ms->smp.dies == 1) {
+ cpu->die_id = 0;
+ }
+
+ if (cpu->socket_id < 0) {
+ error_setg(errp, "CPU socket-id is not set");
+ return;
+ } else if (cpu->socket_id > max_socket) {
+ error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",
+ cpu->socket_id, max_socket);
+ return;
+ }
+ if (cpu->die_id < 0) {
+ error_setg(errp, "CPU die-id is not set");
+ return;
+ } else if (cpu->die_id > ms->smp.dies - 1) {
+ error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
+ cpu->die_id, ms->smp.dies - 1);
+ return;
+ }
+ if (cpu->core_id < 0) {
+ error_setg(errp, "CPU core-id is not set");
+ return;
+ } else if (cpu->core_id > (smp_cores - 1)) {
+ error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
+ cpu->core_id, smp_cores - 1);
+ return;
+ }
+ if (cpu->thread_id < 0) {
+ error_setg(errp, "CPU thread-id is not set");
+ return;
+ } else if (cpu->thread_id > (smp_threads - 1)) {
+ error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
+ cpu->thread_id, smp_threads - 1);
+ return;
+ }
+
+ topo_ids.pkg_id = cpu->socket_id;
+ topo_ids.die_id = cpu->die_id;
+ topo_ids.core_id = cpu->core_id;
+ topo_ids.smt_id = cpu->thread_id;
+ cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
+ }
+
+ cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
+ if (!cpu_slot) {
+ x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
+ error_setg(errp,
+ "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
+ " APIC ID %" PRIu32 ", valid index range 0:%d",
+ topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,
+ cpu->apic_id, ms->possible_cpus->len - 1);
+ return;
+ }
+
+ if (cpu_slot->cpu) {
+ error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
+ idx, cpu->apic_id);
+ return;
+ }
+
+ /* if 'address' properties socket-id/core-id/thread-id are not set, set them
+ * so that machine_query_hotpluggable_cpus would show correct values
+ */
+ /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
+ * once -smp refactoring is complete and there will be CPU private
+ * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
+ x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
+ if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {
+ error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
+ " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,
+ topo_ids.pkg_id);
+ return;
+ }
+ cpu->socket_id = topo_ids.pkg_id;
+
+ if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) {
+ error_setg(errp, "property die-id: %u doesn't match set apic-id:"
+ " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id);
+ return;
+ }
+ cpu->die_id = topo_ids.die_id;
+
+ if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) {
+ error_setg(errp, "property core-id: %u doesn't match set apic-id:"
+ " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id,
+ topo_ids.core_id);
+ return;
+ }
+ cpu->core_id = topo_ids.core_id;
+
+ if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) {
+ error_setg(errp, "property thread-id: %u doesn't match set apic-id:"
+ " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id,
+ topo_ids.smt_id);
+ return;
+ }
+ cpu->thread_id = topo_ids.smt_id;
+
+ /*
+ * kvm_enabled() must go first to ensure that kvm_* references are
+ * not emitted for the linker to consume (kvm_enabled() is
+ * a literal `0` in configurations where kvm_* aren't defined)
+ */
+ if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&
+ !kvm_hv_vpindex_settable()) {
+ error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");
+ return;
+ }
+
+ cs = CPU(cpu);
+ cs->cpu_index = idx;
+
+ numa_cpu_pre_plug(cpu_slot, dev, errp);
+}
+
+static long get_file_size(FILE *f)
+{
+ long where, size;
+
+ /* XXX: on Unix systems, using fstat() probably makes more sense */
+
+ where = ftell(f);
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, where, SEEK_SET);
+
+ return size;
+}
+
+void gsi_handler(void *opaque, int n, int level)
+{
+ GSIState *s = opaque;
+
+ trace_x86_gsi_interrupt(n, level);
+ switch (n) {
+ case 0 ... ISA_NUM_IRQS - 1:
+ if (s->i8259_irq[n]) {
+ /* Under KVM, Kernel will forward to both PIC and IOAPIC */
+ qemu_set_irq(s->i8259_irq[n], level);
+ }
+ /* fall through */
+ case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1:
+#ifdef CONFIG_XEN_EMU
+ /*
+ * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC
+ * routing actually works properly under Xen). And then to
+ * *either* the PIRQ handling or the I/OAPIC depending on
+ * whether the former wants it.
+ */
+ if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) {
+ break;
+ }
+#endif
+ qemu_set_irq(s->ioapic_irq[n], level);
+ break;
+ case IO_APIC_SECONDARY_IRQBASE
+ ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1:
+ qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level);
+ break;
+ }
+}
+
+void ioapic_init_gsi(GSIState *gsi_state, Object *parent)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ unsigned int i;
+
+ assert(parent);
+ if (kvm_ioapic_in_kernel()) {
+ dev = qdev_new(TYPE_KVM_IOAPIC);
+ } else {
+ dev = qdev_new(TYPE_IOAPIC);
+ }
+ object_property_add_child(parent, "ioapic", OBJECT(dev));
+ d = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(d, &error_fatal);
+ sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS);
+
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i);
+ }
+}
+
+DeviceState *ioapic_init_secondary(GSIState *gsi_state)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ unsigned int i;
+
+ dev = qdev_new(TYPE_IOAPIC);
+ d = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(d, &error_fatal);
+ sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS);
+
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i);
+ }
+ return dev;
+}
+
+/*
+ * The entry point into the kernel for PVH boot is different from
+ * the native entry point. The PVH entry is defined by the x86/HVM
+ * direct boot ABI and is available in an ELFNOTE in the kernel binary.
+ *
+ * This function is passed to load_elf() when it is called from
+ * load_elfboot() which then additionally checks for an ELF Note of
+ * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to
+ * parse the PVH entry address from the ELF Note.
+ *
+ * Due to trickery in elf_opts.h, load_elf() is actually available as
+ * load_elf32() or load_elf64() and this routine needs to be able
+ * to deal with being called as 32 or 64 bit.
+ *
+ * The address of the PVH entry point is saved to the 'pvh_start_addr'
+ * global variable. (although the entry point is 32-bit, the kernel
+ * binary can be either 32-bit or 64-bit).
+ */
+static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64)
+{
+ size_t *elf_note_data_addr;
+
+ /* Check if ELF Note header passed in is valid */
+ if (arg1 == NULL) {
+ return 0;
+ }
+
+ if (is64) {
+ struct elf64_note *nhdr64 = (struct elf64_note *)arg1;
+ uint64_t nhdr_size64 = sizeof(struct elf64_note);
+ uint64_t phdr_align = *(uint64_t *)arg2;
+ uint64_t nhdr_namesz = nhdr64->n_namesz;
+
+ elf_note_data_addr =
+ ((void *)nhdr64) + nhdr_size64 +
+ QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
+
+ pvh_start_addr = *elf_note_data_addr;
+ } else {
+ struct elf32_note *nhdr32 = (struct elf32_note *)arg1;
+ uint32_t nhdr_size32 = sizeof(struct elf32_note);
+ uint32_t phdr_align = *(uint32_t *)arg2;
+ uint32_t nhdr_namesz = nhdr32->n_namesz;
+
+ elf_note_data_addr =
+ ((void *)nhdr32) + nhdr_size32 +
+ QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
+
+ pvh_start_addr = *(uint32_t *)elf_note_data_addr;
+ }
+
+ return pvh_start_addr;
+}
+
+static bool load_elfboot(const char *kernel_filename,
+ int kernel_file_size,
+ uint8_t *header,
+ size_t pvh_xen_start_addr,
+ FWCfgState *fw_cfg)
+{
+ uint32_t flags = 0;
+ uint32_t mh_load_addr = 0;
+ uint32_t elf_kernel_size = 0;
+ uint64_t elf_entry;
+ uint64_t elf_low, elf_high;
+ int kernel_size;
+
+ if (ldl_p(header) != 0x464c457f) {
+ return false; /* no elfboot */
+ }
+
+ bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
+ flags = elf_is64 ?
+ ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
+
+ if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
+ error_report("elfboot unsupported flags = %x", flags);
+ exit(1);
+ }
+
+ uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY;
+ kernel_size = load_elf(kernel_filename, read_pvh_start_addr,
+ NULL, &elf_note_type, &elf_entry,
+ &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE,
+ 0, 0);
+
+ if (kernel_size < 0) {
+ error_report("Error while loading elf kernel");
+ exit(1);
+ }
+ mh_load_addr = elf_low;
+ elf_kernel_size = elf_high - elf_low;
+
+ if (pvh_start_addr == 0) {
+ error_report("Error loading uncompressed kernel without PVH ELF Note");
+ exit(1);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
+
+ return true;
+}
+
+void x86_load_linux(X86MachineState *x86ms,
+ FWCfgState *fw_cfg,
+ int acpi_data_size,
+ bool pvh_enabled)
+{
+ bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled;
+ uint16_t protocol;
+ int setup_size, kernel_size, cmdline_size;
+ int dtb_size, setup_data_offset;
+ uint32_t initrd_max;
+ uint8_t header[8192], *setup, *kernel;
+ hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
+ FILE *f;
+ char *vmode;
+ MachineState *machine = MACHINE(x86ms);
+ struct setup_data *setup_data;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *dtb_filename = machine->dtb;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ SevKernelLoaderContext sev_load_ctx = {};
+
+ /* Align to 16 bytes as a paranoia measure */
+ cmdline_size = (strlen(kernel_cmdline) + 16) & ~15;
+
+ /* load the kernel header */
+ f = fopen(kernel_filename, "rb");
+ if (!f) {
+ fprintf(stderr, "qemu: could not open kernel file '%s': %s\n",
+ kernel_filename, strerror(errno));
+ exit(1);
+ }
+
+ kernel_size = get_file_size(f);
+ if (!kernel_size ||
+ fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
+ MIN(ARRAY_SIZE(header), kernel_size)) {
+ fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
+ kernel_filename, strerror(errno));
+ exit(1);
+ }
+
+ /* kernel protocol version */
+ if (ldl_p(header + 0x202) == 0x53726448) {
+ protocol = lduw_p(header + 0x206);
+ } else {
+ /*
+ * This could be a multiboot kernel. If it is, let's stop treating it
+ * like a Linux kernel.
+ * Note: some multiboot images could be in the ELF format (the same of
+ * PVH), so we try multiboot first since we check the multiboot magic
+ * header before to load it.
+ */
+ if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename,
+ kernel_cmdline, kernel_size, header)) {
+ return;
+ }
+ /*
+ * Check if the file is an uncompressed kernel file (ELF) and load it,
+ * saving the PVH entry point used by the x86/HVM direct boot ABI.
+ * If load_elfboot() is successful, populate the fw_cfg info.
+ */
+ if (pvh_enabled &&
+ load_elfboot(kernel_filename, kernel_size,
+ header, pvh_start_addr, fw_cfg)) {
+ fclose(f);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
+ header, sizeof(header));
+
+ /* load initrd */
+ if (initrd_filename) {
+ GMappedFile *mapped_file;
+ gsize initrd_size;
+ gchar *initrd_data;
+ GError *gerr = NULL;
+
+ mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
+ if (!mapped_file) {
+ fprintf(stderr, "qemu: error reading initrd %s: %s\n",
+ initrd_filename, gerr->message);
+ exit(1);
+ }
+ x86ms->initrd_mapped_file = mapped_file;
+
+ initrd_data = g_mapped_file_get_contents(mapped_file);
+ initrd_size = g_mapped_file_get_length(mapped_file);
+ initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
+ if (initrd_size >= initrd_max) {
+ fprintf(stderr, "qemu: initrd is too large, cannot support."
+ "(max: %"PRIu32", need %"PRId64")\n",
+ initrd_max, (uint64_t)initrd_size);
+ exit(1);
+ }
+
+ initrd_addr = (initrd_max - initrd_size) & ~4095;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data,
+ initrd_size);
+ }
+
+ option_rom[nb_option_roms].bootindex = 0;
+ option_rom[nb_option_roms].name = "pvh.bin";
+ nb_option_roms++;
+
+ return;
+ }
+ protocol = 0;
+ }
+
+ if (protocol < 0x200 || !(header[0x211] & 0x01)) {
+ /* Low kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x10000;
+ } else if (protocol < 0x202) {
+ /* High but ancient kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x100000;
+ } else {
+ /* High and recent kernel */
+ real_addr = 0x10000;
+ cmdline_addr = 0x20000;
+ prot_addr = 0x100000;
+ }
+
+ /* highest address for loading the initrd */
+ if (protocol >= 0x20c &&
+ lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) {
+ /*
+ * Linux has supported initrd up to 4 GB for a very long time (2007,
+ * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013),
+ * though it only sets initrd_max to 2 GB to "work around bootloader
+ * bugs". Luckily, QEMU firmware(which does something like bootloader)
+ * has supported this.
+ *
+ * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can
+ * be loaded into any address.
+ *
+ * In addition, initrd_max is uint32_t simply because QEMU doesn't
+ * support the 64-bit boot protocol (specifically the ext_ramdisk_image
+ * field).
+ *
+ * Therefore here just limit initrd_max to UINT32_MAX simply as well.
+ */
+ initrd_max = UINT32_MAX;
+ } else if (protocol >= 0x203) {
+ initrd_max = ldl_p(header + 0x22c);
+ } else {
+ initrd_max = 0x37ffffff;
+ }
+
+ if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) {
+ initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
+ }
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+ sev_load_ctx.cmdline_data = (char *)kernel_cmdline;
+ sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1;
+
+ if (protocol >= 0x202) {
+ stl_p(header + 0x228, cmdline_addr);
+ } else {
+ stw_p(header + 0x20, 0xA33F);
+ stw_p(header + 0x22, cmdline_addr - real_addr);
+ }
+
+ /* handle vga= parameter */
+ vmode = strstr(kernel_cmdline, "vga=");
+ if (vmode) {
+ unsigned int video_mode;
+ const char *end;
+ int ret;
+ /* skip "vga=" */
+ vmode += 4;
+ if (!strncmp(vmode, "normal", 6)) {
+ video_mode = 0xffff;
+ } else if (!strncmp(vmode, "ext", 3)) {
+ video_mode = 0xfffe;
+ } else if (!strncmp(vmode, "ask", 3)) {
+ video_mode = 0xfffd;
+ } else {
+ ret = qemu_strtoui(vmode, &end, 0, &video_mode);
+ if (ret != 0 || (*end && *end != ' ')) {
+ fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n");
+ exit(1);
+ }
+ }
+ stw_p(header + 0x1fa, video_mode);
+ }
+
+ /* loader type */
+ /*
+ * High nybble = B reserved for QEMU; low nybble is revision number.
+ * If this code is substantially changed, you may want to consider
+ * incrementing the revision.
+ */
+ if (protocol >= 0x200) {
+ header[0x210] = 0xB0;
+ }
+ /* heap */
+ if (protocol >= 0x201) {
+ header[0x211] |= 0x80; /* CAN_USE_HEAP */
+ stw_p(header + 0x224, cmdline_addr - real_addr - 0x200);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ GMappedFile *mapped_file;
+ gsize initrd_size;
+ gchar *initrd_data;
+ GError *gerr = NULL;
+
+ if (protocol < 0x200) {
+ fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
+ exit(1);
+ }
+
+ mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
+ if (!mapped_file) {
+ fprintf(stderr, "qemu: error reading initrd %s: %s\n",
+ initrd_filename, gerr->message);
+ exit(1);
+ }
+ x86ms->initrd_mapped_file = mapped_file;
+
+ initrd_data = g_mapped_file_get_contents(mapped_file);
+ initrd_size = g_mapped_file_get_length(mapped_file);
+ if (initrd_size >= initrd_max) {
+ fprintf(stderr, "qemu: initrd is too large, cannot support."
+ "(max: %"PRIu32", need %"PRId64")\n",
+ initrd_max, (uint64_t)initrd_size);
+ exit(1);
+ }
+
+ initrd_addr = (initrd_max - initrd_size) & ~4095;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
+ sev_load_ctx.initrd_data = initrd_data;
+ sev_load_ctx.initrd_size = initrd_size;
+
+ stl_p(header + 0x218, initrd_addr);
+ stl_p(header + 0x21c, initrd_size);
+ }
+
+ /* load kernel and setup */
+ setup_size = header[0x1f1];
+ if (setup_size == 0) {
+ setup_size = 4;
+ }
+ setup_size = (setup_size + 1) * 512;
+ if (setup_size > kernel_size) {
+ fprintf(stderr, "qemu: invalid kernel header\n");
+ exit(1);
+ }
+ kernel_size -= setup_size;
+
+ setup = g_malloc(setup_size);
+ kernel = g_malloc(kernel_size);
+ fseek(f, 0, SEEK_SET);
+ if (fread(setup, 1, setup_size, f) != setup_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ if (fread(kernel, 1, kernel_size, f) != kernel_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ fclose(f);
+
+ /* append dtb to kernel */
+ if (dtb_filename) {
+ if (protocol < 0x209) {
+ fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n");
+ exit(1);
+ }
+
+ dtb_size = get_image_size(dtb_filename);
+ if (dtb_size <= 0) {
+ fprintf(stderr, "qemu: error reading dtb %s: %s\n",
+ dtb_filename, strerror(errno));
+ exit(1);
+ }
+
+ setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16);
+ kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size;
+ kernel = g_realloc(kernel, kernel_size);
+
+ stq_p(header + 0x250, prot_addr + setup_data_offset);
+
+ setup_data = (struct setup_data *)(kernel + setup_data_offset);
+ setup_data->next = 0;
+ setup_data->type = cpu_to_le32(SETUP_DTB);
+ setup_data->len = cpu_to_le32(dtb_size);
+
+ load_image_size(dtb_filename, setup_data->data, dtb_size);
+ }
+
+ /*
+ * If we're starting an encrypted VM, it will be OVMF based, which uses the
+ * efi stub for booting and doesn't require any values to be placed in the
+ * kernel header. We therefore don't update the header so the hash of the
+ * kernel on the other side of the fw_cfg interface matches the hash of the
+ * file the user passed in.
+ */
+ if (!sev_enabled()) {
+ memcpy(setup, header, MIN(sizeof(header), setup_size));
+ }
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
+ sev_load_ctx.kernel_data = (char *)kernel;
+ sev_load_ctx.kernel_size = kernel_size;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
+ sev_load_ctx.setup_data = (char *)setup;
+ sev_load_ctx.setup_size = setup_size;
+
+ if (sev_enabled()) {
+ sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal);
+ }
+
+ option_rom[nb_option_roms].bootindex = 0;
+ option_rom[nb_option_roms].name = "linuxboot.bin";
+ if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) {
+ option_rom[nb_option_roms].name = "linuxboot_dma.bin";
+ }
+ nb_option_roms++;
+}
+
+void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory,
+ MemoryRegion *bios, bool read_only)
+{
+ uint64_t bios_size = memory_region_size(bios);
+ uint64_t isa_bios_size = MIN(bios_size, 128 * KiB);
+
+ memory_region_init_alias(isa_bios, NULL, "isa-bios", bios,
+ bios_size - isa_bios_size, isa_bios_size);
+ memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size,
+ isa_bios, 1);
+ memory_region_set_readonly(isa_bios, read_only);
+}
+
+void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware,
+ MemoryRegion *rom_memory, bool isapc_ram_fw)
+{
+ const char *bios_name;
+ char *filename;
+ int bios_size;
+ ssize_t ret;
+
+ /* BIOS load */
+ bios_name = MACHINE(x86ms)->firmware ?: default_firmware;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size <= 0 ||
+ (bios_size % 65536) != 0) {
+ goto bios_error;
+ }
+ memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size,
+ &error_fatal);
+ if (sev_enabled()) {
+ /*
+ * The concept of a "reset" simply doesn't exist for
+ * confidential computing guests, we have to destroy and
+ * re-launch them instead. So there is no need to register
+ * the firmware as rom to properly re-initialize on reset.
+ * Just go for a straight file load instead.
+ */
+ void *ptr = memory_region_get_ram_ptr(&x86ms->bios);
+ load_image_size(filename, ptr, bios_size);
+ x86_firmware_configure(ptr, bios_size);
+ } else {
+ memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw);
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
+ if (ret != 0) {
+ goto bios_error;
+ }
+ }
+ g_free(filename);
+
+ /* map the last 128KB of the BIOS in ISA space */
+ x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios,
+ !isapc_ram_fw);
+
+ /* map all the bios at the top of memory */
+ memory_region_add_subregion(rom_memory,
+ (uint32_t)(-bios_size),
+ &x86ms->bios);
+ return;
+
+bios_error:
+ fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+ exit(1);
+}
diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c
new file mode 100644
index 0000000000..ab2920522d
--- /dev/null
+++ b/hw/i386/x86-cpu.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2019, 2024 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 "qemu/osdep.h"
+#include "sysemu/whpx.h"
+#include "sysemu/cpu-timers.h"
+#include "trace.h"
+
+#include "hw/i386/x86.h"
+#include "target/i386/cpu.h"
+#include "hw/intc/i8259.h"
+#include "hw/irq.h"
+#include "sysemu/kvm.h"
+
+/* TSC handling */
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpus_get_elapsed_ticks();
+}
+
+/* IRQ handling */
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+ CPUState *cs = first_cpu;
+ X86CPU *cpu = X86_CPU(cs);
+
+ trace_x86_pic_interrupt(irq, level);
+ if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() &&
+ !whpx_apic_in_platform()) {
+ CPU_FOREACH(cs) {
+ cpu = X86_CPU(cs);
+ if (apic_accept_pic_intr(cpu->apic_state)) {
+ apic_deliver_pic_intr(cpu->apic_state, level);
+ }
+ }
+ } else {
+ if (level) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ }
+}
+
+qemu_irq x86_allocate_cpu_irq(void)
+{
+ return qemu_allocate_irq(pic_irq_request, NULL, 0);
+}
+
+int cpu_get_pic_interrupt(CPUX86State *env)
+{
+ X86CPU *cpu = env_archcpu(env);
+ int intno;
+
+ if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) {
+ intno = apic_get_interrupt(cpu->apic_state);
+ if (intno >= 0) {
+ return intno;
+ }
+ /* read the irq from the PIC */
+ if (!apic_accept_pic_intr(cpu->apic_state)) {
+ return -1;
+ }
+ }
+
+ intno = pic_read_irq(isa_pic);
+ return intno;
+}
+
+DeviceState *cpu_get_current_apic(void)
+{
+ if (current_cpu) {
+ X86CPU *cpu = X86_CPU(current_cpu);
+ return cpu->apic_state;
+ } else {
+ return NULL;
+ }
+}
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
index 3d5b51e92d..0b5cc59956 100644
--- a/hw/i386/x86.c
+++ b/hw/i386/x86.c
@@ -22,52 +22,25 @@
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
-#include "qemu/option.h"
-#include "qemu/cutils.h"
#include "qemu/units.h"
-#include "qemu/datadir.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-common.h"
-#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-machine.h"
#include "qapi/visitor.h"
#include "sysemu/qtest.h"
-#include "sysemu/whpx.h"
#include "sysemu/numa.h"
-#include "sysemu/replay.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/cpu-timers.h"
-#include "sysemu/xen.h"
#include "trace.h"
+#include "hw/acpi/aml-build.h"
#include "hw/i386/x86.h"
-#include "target/i386/cpu.h"
#include "hw/i386/topology.h"
-#include "hw/i386/fw_cfg.h"
-#include "hw/intc/i8259.h"
-#include "hw/rtc/mc146818rtc.h"
-#include "target/i386/sev.h"
-#include "hw/acpi/cpu_hotplug.h"
-#include "hw/irq.h"
#include "hw/nmi.h"
-#include "hw/loader.h"
-#include "multiboot.h"
-#include "elf.h"
-#include "standard-headers/asm-x86/bootparam.h"
-#include CONFIG_DEVICES
#include "kvm/kvm_i386.h"
-#ifdef CONFIG_XEN_EMU
-#include "hw/xen/xen.h"
-#include "hw/i386/kvm/xen_evtchn.h"
-#endif
-/* Physical Address of PVH entry point read from kernel ELF NOTE */
-static size_t pvh_start_addr;
-
-static void init_topo_info(X86CPUTopoInfo *topo_info,
- const X86MachineState *x86ms)
+void init_topo_info(X86CPUTopoInfo *topo_info,
+ const X86MachineState *x86ms)
{
MachineState *ms = MACHINE(x86ms);
@@ -94,356 +67,7 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms,
return x86_apicid_from_cpu_idx(&topo_info, cpu_index);
}
-
-void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp)
-{
- Object *cpu = object_new(MACHINE(x86ms)->cpu_type);
-
- if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) {
- goto out;
- }
- qdev_realize(DEVICE(cpu), NULL, errp);
-
-out:
- object_unref(cpu);
-}
-
-void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
-{
- int i;
- const CPUArchIdList *possible_cpus;
- MachineState *ms = MACHINE(x86ms);
- MachineClass *mc = MACHINE_GET_CLASS(x86ms);
-
- x86_cpu_set_default_version(default_cpu_version);
-
- /*
- * Calculates the limit to CPU APIC ID values
- *
- * Limit for the APIC ID value, so that all
- * CPU APIC IDs are < x86ms->apic_id_limit.
- *
- * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create().
- */
- x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms,
- ms->smp.max_cpus - 1) + 1;
-
- /*
- * Can we support APIC ID 255 or higher? With KVM, that requires
- * both in-kernel lapic and X2APIC userspace API.
- *
- * kvm_enabled() must go first to ensure that kvm_* references are
- * not emitted for the linker to consume (kvm_enabled() is
- * a literal `0` in configurations where kvm_* aren't defined)
- */
- if (kvm_enabled() && x86ms->apic_id_limit > 255 &&
- kvm_irqchip_in_kernel() && !kvm_enable_x2apic()) {
- error_report("current -smp configuration requires kernel "
- "irqchip and X2APIC API support.");
- exit(EXIT_FAILURE);
- }
-
- if (kvm_enabled()) {
- kvm_set_max_apic_id(x86ms->apic_id_limit);
- }
-
- if (!kvm_irqchip_in_kernel()) {
- apic_set_max_apic_id(x86ms->apic_id_limit);
- }
-
- possible_cpus = mc->possible_cpu_arch_ids(ms);
- for (i = 0; i < ms->smp.cpus; i++) {
- x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
- }
-}
-
-void x86_rtc_set_cpus_count(ISADevice *s, uint16_t cpus_count)
-{
- MC146818RtcState *rtc = MC146818_RTC(s);
-
- if (cpus_count > 0xff) {
- /*
- * If the number of CPUs can't be represented in 8 bits, the
- * BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just
- * to make old BIOSes fail more predictably.
- */
- mc146818rtc_set_cmos_data(rtc, 0x5f, 0);
- } else {
- mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1);
- }
-}
-
-static int x86_apic_cmp(const void *a, const void *b)
-{
- CPUArchId *apic_a = (CPUArchId *)a;
- CPUArchId *apic_b = (CPUArchId *)b;
-
- return apic_a->arch_id - apic_b->arch_id;
-}
-
-/*
- * returns pointer to CPUArchId descriptor that matches CPU's apic_id
- * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no
- * entry corresponding to CPU's apic_id returns NULL.
- */
-CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
-{
- CPUArchId apic_id, *found_cpu;
-
- apic_id.arch_id = id;
- found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
- ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
- x86_apic_cmp);
- if (found_cpu && idx) {
- *idx = found_cpu - ms->possible_cpus->cpus;
- }
- return found_cpu;
-}
-
-void x86_cpu_plug(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- CPUArchId *found_cpu;
- Error *local_err = NULL;
- X86CPU *cpu = X86_CPU(dev);
- X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
-
- if (x86ms->acpi_dev) {
- hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err);
- if (local_err) {
- goto out;
- }
- }
-
- /* increment the number of CPUs */
- x86ms->boot_cpus++;
- if (x86ms->rtc) {
- x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
- }
- if (x86ms->fw_cfg) {
- fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
- }
-
- found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
- found_cpu->cpu = CPU(dev);
-out:
- error_propagate(errp, local_err);
-}
-
-void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- int idx = -1;
- X86CPU *cpu = X86_CPU(dev);
- X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
-
- if (!x86ms->acpi_dev) {
- error_setg(errp, "CPU hot unplug not supported without ACPI");
- return;
- }
-
- x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
- assert(idx != -1);
- if (idx == 0) {
- error_setg(errp, "Boot CPU is unpluggable");
- return;
- }
-
- hotplug_handler_unplug_request(x86ms->acpi_dev, dev,
- errp);
-}
-
-void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- CPUArchId *found_cpu;
- Error *local_err = NULL;
- X86CPU *cpu = X86_CPU(dev);
- X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
-
- hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);
- if (local_err) {
- goto out;
- }
-
- found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
- found_cpu->cpu = NULL;
- qdev_unrealize(dev);
-
- /* decrement the number of CPUs */
- x86ms->boot_cpus--;
- /* Update the number of CPUs in CMOS */
- x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
- fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
- out:
- error_propagate(errp, local_err);
-}
-
-void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- int idx;
- CPUState *cs;
- CPUArchId *cpu_slot;
- X86CPUTopoIDs topo_ids;
- X86CPU *cpu = X86_CPU(dev);
- CPUX86State *env = &cpu->env;
- MachineState *ms = MACHINE(hotplug_dev);
- X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
- unsigned int smp_cores = ms->smp.cores;
- unsigned int smp_threads = ms->smp.threads;
- X86CPUTopoInfo topo_info;
-
- if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
- error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
- ms->cpu_type);
- return;
- }
-
- if (x86ms->acpi_dev) {
- Error *local_err = NULL;
-
- hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- }
-
- init_topo_info(&topo_info, x86ms);
-
- env->nr_dies = ms->smp.dies;
-
- /*
- * If APIC ID is not set,
- * set it based on socket/die/core/thread properties.
- */
- if (cpu->apic_id == UNASSIGNED_APIC_ID) {
- int max_socket = (ms->smp.max_cpus - 1) /
- smp_threads / smp_cores / ms->smp.dies;
-
- /*
- * die-id was optional in QEMU 4.0 and older, so keep it optional
- * if there's only one die per socket.
- */
- if (cpu->die_id < 0 && ms->smp.dies == 1) {
- cpu->die_id = 0;
- }
-
- if (cpu->socket_id < 0) {
- error_setg(errp, "CPU socket-id is not set");
- return;
- } else if (cpu->socket_id > max_socket) {
- error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",
- cpu->socket_id, max_socket);
- return;
- }
- if (cpu->die_id < 0) {
- error_setg(errp, "CPU die-id is not set");
- return;
- } else if (cpu->die_id > ms->smp.dies - 1) {
- error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
- cpu->die_id, ms->smp.dies - 1);
- return;
- }
- if (cpu->core_id < 0) {
- error_setg(errp, "CPU core-id is not set");
- return;
- } else if (cpu->core_id > (smp_cores - 1)) {
- error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
- cpu->core_id, smp_cores - 1);
- return;
- }
- if (cpu->thread_id < 0) {
- error_setg(errp, "CPU thread-id is not set");
- return;
- } else if (cpu->thread_id > (smp_threads - 1)) {
- error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
- cpu->thread_id, smp_threads - 1);
- return;
- }
-
- topo_ids.pkg_id = cpu->socket_id;
- topo_ids.die_id = cpu->die_id;
- topo_ids.core_id = cpu->core_id;
- topo_ids.smt_id = cpu->thread_id;
- cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
- }
-
- cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
- if (!cpu_slot) {
- x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
- error_setg(errp,
- "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
- " APIC ID %" PRIu32 ", valid index range 0:%d",
- topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,
- cpu->apic_id, ms->possible_cpus->len - 1);
- return;
- }
-
- if (cpu_slot->cpu) {
- error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
- idx, cpu->apic_id);
- return;
- }
-
- /* if 'address' properties socket-id/core-id/thread-id are not set, set them
- * so that machine_query_hotpluggable_cpus would show correct values
- */
- /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
- * once -smp refactoring is complete and there will be CPU private
- * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
- x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
- if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {
- error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
- " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,
- topo_ids.pkg_id);
- return;
- }
- cpu->socket_id = topo_ids.pkg_id;
-
- if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) {
- error_setg(errp, "property die-id: %u doesn't match set apic-id:"
- " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id);
- return;
- }
- cpu->die_id = topo_ids.die_id;
-
- if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) {
- error_setg(errp, "property core-id: %u doesn't match set apic-id:"
- " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id,
- topo_ids.core_id);
- return;
- }
- cpu->core_id = topo_ids.core_id;
-
- if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) {
- error_setg(errp, "property thread-id: %u doesn't match set apic-id:"
- " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id,
- topo_ids.smt_id);
- return;
- }
- cpu->thread_id = topo_ids.smt_id;
-
- /*
- * kvm_enabled() must go first to ensure that kvm_* references are
- * not emitted for the linker to consume (kvm_enabled() is
- * a literal `0` in configurations where kvm_* aren't defined)
- */
- if (kvm_enabled() && hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&
- !kvm_hv_vpindex_settable()) {
- error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");
- return;
- }
-
- cs = CPU(cpu);
- cs->cpu_index = idx;
-
- numa_cpu_pre_plug(cpu_slot, dev, errp);
-}
-
-CpuInstanceProperties
+static CpuInstanceProperties
x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
@@ -453,7 +77,7 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
return possible_cpus->cpus[cpu_index].props;
}
-int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx)
+static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx)
{
X86CPUTopoIDs topo_ids;
X86MachineState *x86ms = X86_MACHINE(ms);
@@ -467,7 +91,7 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx)
return topo_ids.pkg_id % ms->numa_state->num_nodes;
}
-const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms)
+static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms)
{
X86MachineState *x86ms = X86_MACHINE(ms);
unsigned int max_cpus = ms->smp.max_cpus;
@@ -528,675 +152,6 @@ static void x86_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
-static long get_file_size(FILE *f)
-{
- long where, size;
-
- /* XXX: on Unix systems, using fstat() probably makes more sense */
-
- where = ftell(f);
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- fseek(f, where, SEEK_SET);
-
- return size;
-}
-
-/* TSC handling */
-uint64_t cpu_get_tsc(CPUX86State *env)
-{
- return cpus_get_elapsed_ticks();
-}
-
-/* IRQ handling */
-static void pic_irq_request(void *opaque, int irq, int level)
-{
- CPUState *cs = first_cpu;
- X86CPU *cpu = X86_CPU(cs);
-
- trace_x86_pic_interrupt(irq, level);
- if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() &&
- !whpx_apic_in_platform()) {
- CPU_FOREACH(cs) {
- cpu = X86_CPU(cs);
- if (apic_accept_pic_intr(cpu->apic_state)) {
- apic_deliver_pic_intr(cpu->apic_state, level);
- }
- }
- } else {
- if (level) {
- cpu_interrupt(cs, CPU_INTERRUPT_HARD);
- } else {
- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
- }
- }
-}
-
-qemu_irq x86_allocate_cpu_irq(void)
-{
- return qemu_allocate_irq(pic_irq_request, NULL, 0);
-}
-
-int cpu_get_pic_interrupt(CPUX86State *env)
-{
- X86CPU *cpu = env_archcpu(env);
- int intno;
-
- if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) {
- intno = apic_get_interrupt(cpu->apic_state);
- if (intno >= 0) {
- return intno;
- }
- /* read the irq from the PIC */
- if (!apic_accept_pic_intr(cpu->apic_state)) {
- return -1;
- }
- }
-
- intno = pic_read_irq(isa_pic);
- return intno;
-}
-
-DeviceState *cpu_get_current_apic(void)
-{
- if (current_cpu) {
- X86CPU *cpu = X86_CPU(current_cpu);
- return cpu->apic_state;
- } else {
- return NULL;
- }
-}
-
-void gsi_handler(void *opaque, int n, int level)
-{
- GSIState *s = opaque;
-
- trace_x86_gsi_interrupt(n, level);
- switch (n) {
- case 0 ... ISA_NUM_IRQS - 1:
- if (s->i8259_irq[n]) {
- /* Under KVM, Kernel will forward to both PIC and IOAPIC */
- qemu_set_irq(s->i8259_irq[n], level);
- }
- /* fall through */
- case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1:
-#ifdef CONFIG_XEN_EMU
- /*
- * Xen delivers the GSI to the Legacy PIC (not that Legacy PIC
- * routing actually works properly under Xen). And then to
- * *either* the PIRQ handling or the I/OAPIC depending on
- * whether the former wants it.
- */
- if (xen_mode == XEN_EMULATE && xen_evtchn_set_gsi(n, level)) {
- break;
- }
-#endif
- qemu_set_irq(s->ioapic_irq[n], level);
- break;
- case IO_APIC_SECONDARY_IRQBASE
- ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1:
- qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level);
- break;
- }
-}
-
-void ioapic_init_gsi(GSIState *gsi_state, Object *parent)
-{
- DeviceState *dev;
- SysBusDevice *d;
- unsigned int i;
-
- assert(parent);
- if (kvm_ioapic_in_kernel()) {
- dev = qdev_new(TYPE_KVM_IOAPIC);
- } else {
- dev = qdev_new(TYPE_IOAPIC);
- }
- object_property_add_child(parent, "ioapic", OBJECT(dev));
- d = SYS_BUS_DEVICE(dev);
- sysbus_realize_and_unref(d, &error_fatal);
- sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS);
-
- for (i = 0; i < IOAPIC_NUM_PINS; i++) {
- gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i);
- }
-}
-
-DeviceState *ioapic_init_secondary(GSIState *gsi_state)
-{
- DeviceState *dev;
- SysBusDevice *d;
- unsigned int i;
-
- dev = qdev_new(TYPE_IOAPIC);
- d = SYS_BUS_DEVICE(dev);
- sysbus_realize_and_unref(d, &error_fatal);
- sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS);
-
- for (i = 0; i < IOAPIC_NUM_PINS; i++) {
- gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i);
- }
- return dev;
-}
-
-/*
- * The entry point into the kernel for PVH boot is different from
- * the native entry point. The PVH entry is defined by the x86/HVM
- * direct boot ABI and is available in an ELFNOTE in the kernel binary.
- *
- * This function is passed to load_elf() when it is called from
- * load_elfboot() which then additionally checks for an ELF Note of
- * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to
- * parse the PVH entry address from the ELF Note.
- *
- * Due to trickery in elf_opts.h, load_elf() is actually available as
- * load_elf32() or load_elf64() and this routine needs to be able
- * to deal with being called as 32 or 64 bit.
- *
- * The address of the PVH entry point is saved to the 'pvh_start_addr'
- * global variable. (although the entry point is 32-bit, the kernel
- * binary can be either 32-bit or 64-bit).
- */
-static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64)
-{
- size_t *elf_note_data_addr;
-
- /* Check if ELF Note header passed in is valid */
- if (arg1 == NULL) {
- return 0;
- }
-
- if (is64) {
- struct elf64_note *nhdr64 = (struct elf64_note *)arg1;
- uint64_t nhdr_size64 = sizeof(struct elf64_note);
- uint64_t phdr_align = *(uint64_t *)arg2;
- uint64_t nhdr_namesz = nhdr64->n_namesz;
-
- elf_note_data_addr =
- ((void *)nhdr64) + nhdr_size64 +
- QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
-
- pvh_start_addr = *elf_note_data_addr;
- } else {
- struct elf32_note *nhdr32 = (struct elf32_note *)arg1;
- uint32_t nhdr_size32 = sizeof(struct elf32_note);
- uint32_t phdr_align = *(uint32_t *)arg2;
- uint32_t nhdr_namesz = nhdr32->n_namesz;
-
- elf_note_data_addr =
- ((void *)nhdr32) + nhdr_size32 +
- QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
-
- pvh_start_addr = *(uint32_t *)elf_note_data_addr;
- }
-
- return pvh_start_addr;
-}
-
-static bool load_elfboot(const char *kernel_filename,
- int kernel_file_size,
- uint8_t *header,
- size_t pvh_xen_start_addr,
- FWCfgState *fw_cfg)
-{
- uint32_t flags = 0;
- uint32_t mh_load_addr = 0;
- uint32_t elf_kernel_size = 0;
- uint64_t elf_entry;
- uint64_t elf_low, elf_high;
- int kernel_size;
-
- if (ldl_p(header) != 0x464c457f) {
- return false; /* no elfboot */
- }
-
- bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
- flags = elf_is64 ?
- ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
-
- if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
- error_report("elfboot unsupported flags = %x", flags);
- exit(1);
- }
-
- uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY;
- kernel_size = load_elf(kernel_filename, read_pvh_start_addr,
- NULL, &elf_note_type, &elf_entry,
- &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE,
- 0, 0);
-
- if (kernel_size < 0) {
- error_report("Error while loading elf kernel");
- exit(1);
- }
- mh_load_addr = elf_low;
- elf_kernel_size = elf_high - elf_low;
-
- if (pvh_start_addr == 0) {
- error_report("Error loading uncompressed kernel without PVH ELF Note");
- exit(1);
- }
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
-
- return true;
-}
-
-void x86_load_linux(X86MachineState *x86ms,
- FWCfgState *fw_cfg,
- int acpi_data_size,
- bool pvh_enabled)
-{
- bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled;
- uint16_t protocol;
- int setup_size, kernel_size, cmdline_size;
- int dtb_size, setup_data_offset;
- uint32_t initrd_max;
- uint8_t header[8192], *setup, *kernel;
- hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
- FILE *f;
- char *vmode;
- MachineState *machine = MACHINE(x86ms);
- struct setup_data *setup_data;
- const char *kernel_filename = machine->kernel_filename;
- const char *initrd_filename = machine->initrd_filename;
- const char *dtb_filename = machine->dtb;
- const char *kernel_cmdline = machine->kernel_cmdline;
- SevKernelLoaderContext sev_load_ctx = {};
-
- /* Align to 16 bytes as a paranoia measure */
- cmdline_size = (strlen(kernel_cmdline) + 16) & ~15;
-
- /* load the kernel header */
- f = fopen(kernel_filename, "rb");
- if (!f) {
- fprintf(stderr, "qemu: could not open kernel file '%s': %s\n",
- kernel_filename, strerror(errno));
- exit(1);
- }
-
- kernel_size = get_file_size(f);
- if (!kernel_size ||
- fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
- MIN(ARRAY_SIZE(header), kernel_size)) {
- fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
- kernel_filename, strerror(errno));
- exit(1);
- }
-
- /* kernel protocol version */
- if (ldl_p(header + 0x202) == 0x53726448) {
- protocol = lduw_p(header + 0x206);
- } else {
- /*
- * This could be a multiboot kernel. If it is, let's stop treating it
- * like a Linux kernel.
- * Note: some multiboot images could be in the ELF format (the same of
- * PVH), so we try multiboot first since we check the multiboot magic
- * header before to load it.
- */
- if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename,
- kernel_cmdline, kernel_size, header)) {
- return;
- }
- /*
- * Check if the file is an uncompressed kernel file (ELF) and load it,
- * saving the PVH entry point used by the x86/HVM direct boot ABI.
- * If load_elfboot() is successful, populate the fw_cfg info.
- */
- if (pvh_enabled &&
- load_elfboot(kernel_filename, kernel_size,
- header, pvh_start_addr, fw_cfg)) {
- fclose(f);
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
- strlen(kernel_cmdline) + 1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
- header, sizeof(header));
-
- /* load initrd */
- if (initrd_filename) {
- GMappedFile *mapped_file;
- gsize initrd_size;
- gchar *initrd_data;
- GError *gerr = NULL;
-
- mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
- if (!mapped_file) {
- fprintf(stderr, "qemu: error reading initrd %s: %s\n",
- initrd_filename, gerr->message);
- exit(1);
- }
- x86ms->initrd_mapped_file = mapped_file;
-
- initrd_data = g_mapped_file_get_contents(mapped_file);
- initrd_size = g_mapped_file_get_length(mapped_file);
- initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
- if (initrd_size >= initrd_max) {
- fprintf(stderr, "qemu: initrd is too large, cannot support."
- "(max: %"PRIu32", need %"PRId64")\n",
- initrd_max, (uint64_t)initrd_size);
- exit(1);
- }
-
- initrd_addr = (initrd_max - initrd_size) & ~4095;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data,
- initrd_size);
- }
-
- option_rom[nb_option_roms].bootindex = 0;
- option_rom[nb_option_roms].name = "pvh.bin";
- nb_option_roms++;
-
- return;
- }
- protocol = 0;
- }
-
- if (protocol < 0x200 || !(header[0x211] & 0x01)) {
- /* Low kernel */
- real_addr = 0x90000;
- cmdline_addr = 0x9a000 - cmdline_size;
- prot_addr = 0x10000;
- } else if (protocol < 0x202) {
- /* High but ancient kernel */
- real_addr = 0x90000;
- cmdline_addr = 0x9a000 - cmdline_size;
- prot_addr = 0x100000;
- } else {
- /* High and recent kernel */
- real_addr = 0x10000;
- cmdline_addr = 0x20000;
- prot_addr = 0x100000;
- }
-
- /* highest address for loading the initrd */
- if (protocol >= 0x20c &&
- lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) {
- /*
- * Linux has supported initrd up to 4 GB for a very long time (2007,
- * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013),
- * though it only sets initrd_max to 2 GB to "work around bootloader
- * bugs". Luckily, QEMU firmware(which does something like bootloader)
- * has supported this.
- *
- * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can
- * be loaded into any address.
- *
- * In addition, initrd_max is uint32_t simply because QEMU doesn't
- * support the 64-bit boot protocol (specifically the ext_ramdisk_image
- * field).
- *
- * Therefore here just limit initrd_max to UINT32_MAX simply as well.
- */
- initrd_max = UINT32_MAX;
- } else if (protocol >= 0x203) {
- initrd_max = ldl_p(header + 0x22c);
- } else {
- initrd_max = 0x37ffffff;
- }
-
- if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) {
- initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
- }
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
- sev_load_ctx.cmdline_data = (char *)kernel_cmdline;
- sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1;
-
- if (protocol >= 0x202) {
- stl_p(header + 0x228, cmdline_addr);
- } else {
- stw_p(header + 0x20, 0xA33F);
- stw_p(header + 0x22, cmdline_addr - real_addr);
- }
-
- /* handle vga= parameter */
- vmode = strstr(kernel_cmdline, "vga=");
- if (vmode) {
- unsigned int video_mode;
- const char *end;
- int ret;
- /* skip "vga=" */
- vmode += 4;
- if (!strncmp(vmode, "normal", 6)) {
- video_mode = 0xffff;
- } else if (!strncmp(vmode, "ext", 3)) {
- video_mode = 0xfffe;
- } else if (!strncmp(vmode, "ask", 3)) {
- video_mode = 0xfffd;
- } else {
- ret = qemu_strtoui(vmode, &end, 0, &video_mode);
- if (ret != 0 || (*end && *end != ' ')) {
- fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n");
- exit(1);
- }
- }
- stw_p(header + 0x1fa, video_mode);
- }
-
- /* loader type */
- /*
- * High nybble = B reserved for QEMU; low nybble is revision number.
- * If this code is substantially changed, you may want to consider
- * incrementing the revision.
- */
- if (protocol >= 0x200) {
- header[0x210] = 0xB0;
- }
- /* heap */
- if (protocol >= 0x201) {
- header[0x211] |= 0x80; /* CAN_USE_HEAP */
- stw_p(header + 0x224, cmdline_addr - real_addr - 0x200);
- }
-
- /* load initrd */
- if (initrd_filename) {
- GMappedFile *mapped_file;
- gsize initrd_size;
- gchar *initrd_data;
- GError *gerr = NULL;
-
- if (protocol < 0x200) {
- fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
- exit(1);
- }
-
- mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
- if (!mapped_file) {
- fprintf(stderr, "qemu: error reading initrd %s: %s\n",
- initrd_filename, gerr->message);
- exit(1);
- }
- x86ms->initrd_mapped_file = mapped_file;
-
- initrd_data = g_mapped_file_get_contents(mapped_file);
- initrd_size = g_mapped_file_get_length(mapped_file);
- if (initrd_size >= initrd_max) {
- fprintf(stderr, "qemu: initrd is too large, cannot support."
- "(max: %"PRIu32", need %"PRId64")\n",
- initrd_max, (uint64_t)initrd_size);
- exit(1);
- }
-
- initrd_addr = (initrd_max - initrd_size) & ~4095;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
- sev_load_ctx.initrd_data = initrd_data;
- sev_load_ctx.initrd_size = initrd_size;
-
- stl_p(header + 0x218, initrd_addr);
- stl_p(header + 0x21c, initrd_size);
- }
-
- /* load kernel and setup */
- setup_size = header[0x1f1];
- if (setup_size == 0) {
- setup_size = 4;
- }
- setup_size = (setup_size + 1) * 512;
- if (setup_size > kernel_size) {
- fprintf(stderr, "qemu: invalid kernel header\n");
- exit(1);
- }
- kernel_size -= setup_size;
-
- setup = g_malloc(setup_size);
- kernel = g_malloc(kernel_size);
- fseek(f, 0, SEEK_SET);
- if (fread(setup, 1, setup_size, f) != setup_size) {
- fprintf(stderr, "fread() failed\n");
- exit(1);
- }
- if (fread(kernel, 1, kernel_size, f) != kernel_size) {
- fprintf(stderr, "fread() failed\n");
- exit(1);
- }
- fclose(f);
-
- /* append dtb to kernel */
- if (dtb_filename) {
- if (protocol < 0x209) {
- fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n");
- exit(1);
- }
-
- dtb_size = get_image_size(dtb_filename);
- if (dtb_size <= 0) {
- fprintf(stderr, "qemu: error reading dtb %s: %s\n",
- dtb_filename, strerror(errno));
- exit(1);
- }
-
- setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16);
- kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size;
- kernel = g_realloc(kernel, kernel_size);
-
- stq_p(header + 0x250, prot_addr + setup_data_offset);
-
- setup_data = (struct setup_data *)(kernel + setup_data_offset);
- setup_data->next = 0;
- setup_data->type = cpu_to_le32(SETUP_DTB);
- setup_data->len = cpu_to_le32(dtb_size);
-
- load_image_size(dtb_filename, setup_data->data, dtb_size);
- }
-
- /*
- * If we're starting an encrypted VM, it will be OVMF based, which uses the
- * efi stub for booting and doesn't require any values to be placed in the
- * kernel header. We therefore don't update the header so the hash of the
- * kernel on the other side of the fw_cfg interface matches the hash of the
- * file the user passed in.
- */
- if (!sev_enabled()) {
- memcpy(setup, header, MIN(sizeof(header), setup_size));
- }
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
- sev_load_ctx.kernel_data = (char *)kernel;
- sev_load_ctx.kernel_size = kernel_size;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
- sev_load_ctx.setup_data = (char *)setup;
- sev_load_ctx.setup_size = setup_size;
-
- if (sev_enabled()) {
- sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal);
- }
-
- option_rom[nb_option_roms].bootindex = 0;
- option_rom[nb_option_roms].name = "linuxboot.bin";
- if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) {
- option_rom[nb_option_roms].name = "linuxboot_dma.bin";
- }
- nb_option_roms++;
-}
-
-void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
- MemoryRegion *rom_memory, bool isapc_ram_fw)
-{
- const char *bios_name;
- char *filename;
- MemoryRegion *bios, *isa_bios;
- int bios_size, isa_bios_size;
- ssize_t ret;
-
- /* BIOS load */
- bios_name = ms->firmware ?: default_firmware;
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
- if (filename) {
- bios_size = get_image_size(filename);
- } else {
- bios_size = -1;
- }
- if (bios_size <= 0 ||
- (bios_size % 65536) != 0) {
- goto bios_error;
- }
- bios = g_malloc(sizeof(*bios));
- memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
- if (sev_enabled()) {
- /*
- * The concept of a "reset" simply doesn't exist for
- * confidential computing guests, we have to destroy and
- * re-launch them instead. So there is no need to register
- * the firmware as rom to properly re-initialize on reset.
- * Just go for a straight file load instead.
- */
- void *ptr = memory_region_get_ram_ptr(bios);
- load_image_size(filename, ptr, bios_size);
- x86_firmware_configure(ptr, bios_size);
- } else {
- if (!isapc_ram_fw) {
- memory_region_set_readonly(bios, true);
- }
- ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
- if (ret != 0) {
- goto bios_error;
- }
- }
- g_free(filename);
-
- /* map the last 128KB of the BIOS in ISA space */
- isa_bios_size = MIN(bios_size, 128 * KiB);
- isa_bios = g_malloc(sizeof(*isa_bios));
- memory_region_init_alias(isa_bios, NULL, "isa-bios", bios,
- bios_size - isa_bios_size, isa_bios_size);
- memory_region_add_subregion_overlap(rom_memory,
- 0x100000 - isa_bios_size,
- isa_bios,
- 1);
- if (!isapc_ram_fw) {
- memory_region_set_readonly(isa_bios, true);
- }
-
- /* map all the bios at the top of memory */
- memory_region_add_subregion(rom_memory,
- (uint32_t)(-bios_size),
- bios);
- return;
-
-bios_error:
- fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
- exit(1);
-}
-
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
{
bool smm_available = false;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index e8cb2dac92..08d9218455 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1623,11 +1623,24 @@ static bool cmd_read_native_max(IDEState *s, uint8_t cmd)
/* Refuse if no sectors are addressable (e.g. medium not inserted) */
if (s->nb_sectors == 0) {
ide_abort_command(s);
- return true;
- }
+ } else {
+ /*
+ * Save the active drive parameters, which may have been
+ * limited from their native counterparts by, e.g., INITIALIZE
+ * DEVICE PARAMETERS or SET MAX ADDRESS.
+ */
+ const int aheads = s->heads;
+ const int asectors = s->sectors;
- ide_cmd_lba48_transform(s, lba48);
- ide_set_sector(s, s->nb_sectors - 1);
+ s->heads = s->drive_heads;
+ s->sectors = s->drive_sectors;
+
+ ide_cmd_lba48_transform(s, lba48);
+ ide_set_sector(s, s->nb_sectors - 1);
+
+ s->heads = aheads;
+ s->sectors = asectors;
+ }
return true;
}
diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c
index 941f163d36..54a15d2441 100644
--- a/hw/input/tsc2005.c
+++ b/hw/input/tsc2005.c
@@ -28,10 +28,10 @@
#include "migration/vmstate.h"
#include "trace.h"
-#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10)))
+#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10)))
typedef struct {
- qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */
+ qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */
QEMUTimer *timer;
uint16_t model;
@@ -63,7 +63,7 @@ typedef struct {
} TSC2005State;
enum {
- TSC_MODE_XYZ_SCAN = 0x0,
+ TSC_MODE_XYZ_SCAN = 0x0,
TSC_MODE_XY_SCAN,
TSC_MODE_X,
TSC_MODE_Y,
@@ -82,100 +82,100 @@ enum {
};
static const uint16_t mode_regs[16] = {
- 0xf000, /* X, Y, Z scan */
- 0xc000, /* X, Y scan */
- 0x8000, /* X */
- 0x4000, /* Y */
- 0x3000, /* Z */
- 0x0800, /* AUX */
- 0x0400, /* TEMP1 */
- 0x0200, /* TEMP2 */
- 0x0800, /* AUX scan */
- 0x0040, /* X test */
- 0x0020, /* Y test */
- 0x0080, /* Short-circuit test */
- 0x0000, /* Reserved */
- 0x0000, /* X+, X- drivers */
- 0x0000, /* Y+, Y- drivers */
- 0x0000, /* Y+, X- drivers */
+ 0xf000, /* X, Y, Z scan */
+ 0xc000, /* X, Y scan */
+ 0x8000, /* X */
+ 0x4000, /* Y */
+ 0x3000, /* Z */
+ 0x0800, /* AUX */
+ 0x0400, /* TEMP1 */
+ 0x0200, /* TEMP2 */
+ 0x0800, /* AUX scan */
+ 0x0040, /* X test */
+ 0x0020, /* Y test */
+ 0x0080, /* Short-circuit test */
+ 0x0000, /* Reserved */
+ 0x0000, /* X+, X- drivers */
+ 0x0000, /* Y+, Y- drivers */
+ 0x0000, /* Y+, X- drivers */
};
-#define X_TRANSFORM(s) \
+#define X_TRANSFORM(s) \
((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
-#define Y_TRANSFORM(s) \
+#define Y_TRANSFORM(s) \
((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
-#define Z1_TRANSFORM(s) \
+#define Z1_TRANSFORM(s) \
((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
-#define Z2_TRANSFORM(s) \
+#define Z2_TRANSFORM(s) \
((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
-#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */
-#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */
-#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */
+#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */
+#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */
+#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */
static uint16_t tsc2005_read(TSC2005State *s, int reg)
{
uint16_t ret;
switch (reg) {
- case 0x0: /* X */
+ case 0x0: /* X */
s->dav &= ~mode_regs[TSC_MODE_X];
return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
(s->noise & 3);
- case 0x1: /* Y */
+ case 0x1: /* Y */
s->dav &= ~mode_regs[TSC_MODE_Y];
- s->noise ++;
+ s->noise++;
return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
(s->noise & 3);
- case 0x2: /* Z1 */
+ case 0x2: /* Z1 */
s->dav &= 0xdfff;
return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
(s->noise & 3);
- case 0x3: /* Z2 */
+ case 0x3: /* Z2 */
s->dav &= 0xefff;
return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
(s->noise & 3);
- case 0x4: /* AUX */
+ case 0x4: /* AUX */
s->dav &= ~mode_regs[TSC_MODE_AUX];
return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
- case 0x5: /* TEMP1 */
+ case 0x5: /* TEMP1 */
s->dav &= ~mode_regs[TSC_MODE_TEMP1];
return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
(s->noise & 5);
- case 0x6: /* TEMP2 */
+ case 0x6: /* TEMP2 */
s->dav &= 0xdfff;
s->dav &= ~mode_regs[TSC_MODE_TEMP2];
return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
(s->noise & 3);
- case 0x7: /* Status */
+ case 0x7: /* Status */
ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
mode_regs[TSC_MODE_TS_TEST]);
s->reset = true;
return ret;
- case 0x8: /* AUX high threshold */
+ case 0x8: /* AUX high threshold */
return s->aux_thr[1];
- case 0x9: /* AUX low threshold */
+ case 0x9: /* AUX low threshold */
return s->aux_thr[0];
- case 0xa: /* TEMP high threshold */
+ case 0xa: /* TEMP high threshold */
return s->temp_thr[1];
- case 0xb: /* TEMP low threshold */
+ case 0xb: /* TEMP low threshold */
return s->temp_thr[0];
- case 0xc: /* CFR0 */
+ case 0xc: /* CFR0 */
return (s->pressure << 15) | ((!s->busy) << 14) |
- (s->nextprecision << 13) | s->timing[0];
- case 0xd: /* CFR1 */
+ (s->nextprecision << 13) | s->timing[0];
+ case 0xd: /* CFR1 */
return s->timing[1];
- case 0xe: /* CFR2 */
+ case 0xe: /* CFR2 */
return (s->pin_func << 14) | s->filter;
- case 0xf: /* Function select status */
+ case 0xf: /* Function select status */
return s->function >= 0 ? 1 << s->function : 0;
}
@@ -200,13 +200,14 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
s->temp_thr[0] = data;
break;
- case 0xc: /* CFR0 */
+ case 0xc: /* CFR0 */
s->host_mode = (data >> 15) != 0;
if (s->enabled != !(data & 0x4000)) {
s->enabled = !(data & 0x4000);
trace_tsc2005_sense(s->enabled ? "enabled" : "disabled");
- if (s->busy && !s->enabled)
+ if (s->busy && !s->enabled) {
timer_del(s->timer);
+ }
s->busy = s->busy && s->enabled;
}
s->nextprecision = (data >> 13) & 1;
@@ -216,10 +217,10 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
"tsc2005_write: illegal conversion clock setting\n");
}
break;
- case 0xd: /* CFR1 */
+ case 0xd: /* CFR1 */
s->timing[1] = data & 0xf07;
break;
- case 0xe: /* CFR2 */
+ case 0xe: /* CFR2 */
s->pin_func = (data >> 14) & 3;
s->filter = data & 0x3fff;
break;
@@ -258,10 +259,12 @@ static void tsc2005_pin_update(TSC2005State *s)
switch (s->nextfunction) {
case TSC_MODE_XYZ_SCAN:
case TSC_MODE_XY_SCAN:
- if (!s->host_mode && s->dav)
+ if (!s->host_mode && s->dav) {
s->enabled = false;
- if (!s->pressure)
+ }
+ if (!s->pressure) {
return;
+ }
/* Fall through */
case TSC_MODE_AUX_SCAN:
break;
@@ -269,8 +272,9 @@ static void tsc2005_pin_update(TSC2005State *s)
case TSC_MODE_X:
case TSC_MODE_Y:
case TSC_MODE_Z:
- if (!s->pressure)
+ if (!s->pressure) {
return;
+ }
/* Fall through */
case TSC_MODE_AUX:
case TSC_MODE_TEMP1:
@@ -278,8 +282,9 @@ static void tsc2005_pin_update(TSC2005State *s)
case TSC_MODE_X_TEST:
case TSC_MODE_Y_TEST:
case TSC_MODE_TS_TEST:
- if (s->dav)
+ if (s->dav) {
s->enabled = false;
+ }
break;
case TSC_MODE_RESERVED:
@@ -290,13 +295,14 @@ static void tsc2005_pin_update(TSC2005State *s)
return;
}
- if (!s->enabled || s->busy)
+ if (!s->enabled || s->busy) {
return;
+ }
s->busy = true;
s->precision = s->nextprecision;
s->function = s->nextfunction;
- s->pdst = !s->pnd0; /* Synchronised on internal clock */
+ s->pdst = !s->pnd0; /* Synchronised on internal clock */
expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
(NANOSECONDS_PER_SECOND >> 7);
timer_mod(s->timer, expires);
@@ -331,7 +337,7 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
TSC2005State *s = opaque;
uint32_t ret = 0;
- switch (s->state ++) {
+ switch (s->state++) {
case 0:
if (value & 0x80) {
/* Command */
@@ -343,8 +349,9 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
if (s->enabled != !(value & 1)) {
s->enabled = !(value & 1);
trace_tsc2005_sense(s->enabled ? "enabled" : "disabled");
- if (s->busy && !s->enabled)
+ if (s->busy && !s->enabled) {
timer_del(s->timer);
+ }
s->busy = s->busy && s->enabled;
}
tsc2005_pin_update(s);
@@ -368,10 +375,11 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
break;
case 1:
- if (s->command)
+ if (s->command) {
ret = (s->data >> 8) & 0xff;
- else
+ } else {
s->data |= value << 8;
+ }
break;
case 2:
@@ -406,14 +414,18 @@ uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
static void tsc2005_timer_tick(void *opaque)
{
TSC2005State *s = opaque;
+ unsigned int function = s->function;
+
+ assert(function < ARRAY_SIZE(mode_regs));
/* Timer ticked -- a set of conversions has been finished. */
- if (!s->busy)
+ if (!s->busy) {
return;
+ }
s->busy = false;
- s->dav |= mode_regs[s->function];
+ s->dav |= mode_regs[function];
s->function = -1;
tsc2005_pin_update(s);
}
@@ -435,8 +447,9 @@ static void tsc2005_touchscreen_event(void *opaque,
* signaling TS events immediately, but for now we simulate
* the first conversion delay for sake of correctness.
*/
- if (p != s->pressure)
+ if (p != s->pressure) {
tsc2005_pin_update(s);
+ }
}
static int tsc2005_post_load(void *opaque, int version_id)
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index ad59abebaa..58b6d3a710 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -87,7 +87,7 @@ config GOLDFISH_PIC
config M68K_IRQC
bool
-config LOONGARCH_IPI
+config LOONGSON_IPI
bool
config LOONGARCH_PCH_PIC
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 074cf50af2..e4b8437f8b 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -1658,7 +1658,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
*data = s->h_apr[gic_get_vcpu_real_id(cpu)];
} else if (gic_cpu_ns_access(s, cpu, attrs)) {
/* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
- *data = gic_apr_ns_view(s, regno, cpu);
+ *data = gic_apr_ns_view(s, cpu, regno);
} else {
*data = s->apr[regno][cpu];
}
@@ -1746,7 +1746,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
s->h_apr[gic_get_vcpu_real_id(cpu)] = value;
} else if (gic_cpu_ns_access(s, cpu, attrs)) {
/* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
- gic_apr_write_ns_view(s, regno, cpu, value);
+ gic_apr_write_ns_view(s, cpu, regno, value);
} else {
s->apr[regno][cpu] = value;
}
diff --git a/hw/intc/ioapic-stub.c b/hw/intc/ioapic-stub.c
new file mode 100644
index 0000000000..4dcd86248d
--- /dev/null
+++ b/hw/intc/ioapic-stub.c
@@ -0,0 +1,29 @@
+/*
+ * ioapic.c IOAPIC emulation logic
+ *
+ * Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ * Split the ioapic logic from apic.c
+ * Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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 "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/intc/ioapic.h"
+
+void ioapic_eoi_broadcast(int vector)
+{
+}
diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c
index a184112b09..44b3b9c138 100644
--- a/hw/intc/loongarch_ipi.c
+++ b/hw/intc/loongarch_ipi.c
@@ -6,6 +6,7 @@
*/
#include "qemu/osdep.h"
+#include "hw/boards.h"
#include "hw/sysbus.h"
#include "hw/intc/loongarch_ipi.h"
#include "hw/irq.h"
@@ -13,9 +14,8 @@
#include "qapi/error.h"
#include "qemu/log.h"
#include "exec/address-spaces.h"
-#include "hw/loongarch/virt.h"
#include "migration/vmstate.h"
-#include "target/loongarch/internals.h"
+#include "target/loongarch/cpu.h"
#include "trace.h"
static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr,
@@ -122,11 +122,6 @@ static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs)
CPUState *cs;
cpuid = extract32(val, 16, 10);
- if (cpuid >= LOONGARCH_MAX_CPUS) {
- trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid);
- return MEMTX_DECODE_ERROR;
- }
-
cs = ipi_getcpu(cpuid);
if (cs == NULL) {
return MEMTX_DECODE_ERROR;
@@ -146,11 +141,6 @@ static MemTxResult any_send(uint64_t val, MemTxAttrs attrs)
CPUState *cs;
cpuid = extract32(val, 16, 10);
- if (cpuid >= LOONGARCH_MAX_CPUS) {
- trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid);
- return MEMTX_DECODE_ERROR;
- }
-
cs = ipi_getcpu(cpuid);
if (cs == NULL) {
return MEMTX_DECODE_ERROR;
@@ -201,11 +191,6 @@ static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
break;
case IOCSR_IPI_SEND:
cpuid = extract32(val, 16, 10);
- if (cpuid >= LOONGARCH_MAX_CPUS) {
- trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid);
- return MEMTX_DECODE_ERROR;
- }
-
/* IPI status vector */
vector = extract8(val, 0, 5);
cs = ipi_getcpu(cpuid);
diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c
new file mode 100644
index 0000000000..93cc50a37a
--- /dev/null
+++ b/hw/intc/loongson_ipi.c
@@ -0,0 +1,368 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson ipi interrupt support
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "hw/intc/loongson_ipi.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+#include "migration/vmstate.h"
+#ifdef TARGET_LOONGARCH64
+#include "target/loongarch/cpu.h"
+#endif
+#ifdef TARGET_MIPS
+#include "target/mips/cpu.h"
+#endif
+#include "trace.h"
+
+static MemTxResult loongson_ipi_readl(void *opaque, hwaddr addr,
+ uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ IPICore *s;
+ LoongsonIPI *ipi = opaque;
+ uint64_t ret = 0;
+ int index = 0;
+
+ s = &ipi->cpu[attrs.requester_id];
+ addr &= 0xff;
+ switch (addr) {
+ case CORE_STATUS_OFF:
+ ret = s->status;
+ break;
+ case CORE_EN_OFF:
+ ret = s->en;
+ break;
+ case CORE_SET_OFF:
+ ret = 0;
+ break;
+ case CORE_CLEAR_OFF:
+ ret = 0;
+ break;
+ case CORE_BUF_20 ... CORE_BUF_38 + 4:
+ index = (addr - CORE_BUF_20) >> 2;
+ ret = s->buf[index];
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
+ break;
+ }
+
+ trace_loongson_ipi_read(size, (uint64_t)addr, ret);
+ *data = ret;
+ return MEMTX_OK;
+}
+
+static AddressSpace *get_cpu_iocsr_as(CPUState *cpu)
+{
+#ifdef TARGET_LOONGARCH64
+ return LOONGARCH_CPU(cpu)->env.address_space_iocsr;
+#endif
+#ifdef TARGET_MIPS
+ if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) {
+ return &MIPS_CPU(cpu)->env.iocsr.as;
+ }
+#endif
+ return NULL;
+}
+
+static MemTxResult send_ipi_data(CPUState *cpu, uint64_t val, hwaddr addr,
+ MemTxAttrs attrs)
+{
+ int i, mask = 0, data = 0;
+ AddressSpace *iocsr_as = get_cpu_iocsr_as(cpu);
+
+ if (!iocsr_as) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /*
+ * bit 27-30 is mask for byte writing,
+ * if the mask is 0, we need not to do anything.
+ */
+ if ((val >> 27) & 0xf) {
+ data = address_space_ldl(iocsr_as, addr, attrs, NULL);
+ for (i = 0; i < 4; i++) {
+ /* get mask for byte writing */
+ if (val & (0x1 << (27 + i))) {
+ mask |= 0xff << (i * 8);
+ }
+ }
+ }
+
+ data &= mask;
+ data |= (val >> 32) & ~mask;
+ address_space_stl(iocsr_as, addr, data, attrs, NULL);
+
+ return MEMTX_OK;
+}
+
+static int archid_cmp(const void *a, const void *b)
+{
+ CPUArchId *archid_a = (CPUArchId *)a;
+ CPUArchId *archid_b = (CPUArchId *)b;
+
+ return archid_a->arch_id - archid_b->arch_id;
+}
+
+static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id)
+{
+ CPUArchId apic_id, *found_cpu;
+
+ apic_id.arch_id = id;
+ found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
+ ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
+ archid_cmp);
+
+ return found_cpu;
+}
+
+static CPUState *ipi_getcpu(int arch_id)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ CPUArchId *archid;
+
+ archid = find_cpu_by_archid(machine, arch_id);
+ if (archid) {
+ return CPU(archid->cpu);
+ }
+
+ return NULL;
+}
+
+static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs)
+{
+ uint32_t cpuid;
+ hwaddr addr;
+ CPUState *cs;
+
+ cpuid = extract32(val, 16, 10);
+ cs = ipi_getcpu(cpuid);
+ if (cs == NULL) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /* override requester_id */
+ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
+ attrs.requester_id = cs->cpu_index;
+ return send_ipi_data(cs, val, addr, attrs);
+}
+
+static MemTxResult any_send(uint64_t val, MemTxAttrs attrs)
+{
+ uint32_t cpuid;
+ hwaddr addr;
+ CPUState *cs;
+
+ cpuid = extract32(val, 16, 10);
+ cs = ipi_getcpu(cpuid);
+ if (cs == NULL) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /* override requester_id */
+ addr = val & 0xffff;
+ attrs.requester_id = cs->cpu_index;
+ return send_ipi_data(cs, val, addr, attrs);
+}
+
+static MemTxResult loongson_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
+{
+ LoongsonIPI *ipi = opaque;
+ IPICore *s;
+ int index = 0;
+ uint32_t cpuid;
+ uint8_t vector;
+ CPUState *cs;
+
+ s = &ipi->cpu[attrs.requester_id];
+ addr &= 0xff;
+ trace_loongson_ipi_write(size, (uint64_t)addr, val);
+ switch (addr) {
+ case CORE_STATUS_OFF:
+ qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
+ break;
+ case CORE_EN_OFF:
+ s->en = val;
+ break;
+ case CORE_SET_OFF:
+ s->status |= val;
+ if (s->status != 0 && (s->status & s->en) != 0) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+ case CORE_CLEAR_OFF:
+ s->status &= ~val;
+ if (s->status == 0 && s->en != 0) {
+ qemu_irq_lower(s->irq);
+ }
+ break;
+ case CORE_BUF_20 ... CORE_BUF_38 + 4:
+ index = (addr - CORE_BUF_20) >> 2;
+ s->buf[index] = val;
+ break;
+ case IOCSR_IPI_SEND:
+ cpuid = extract32(val, 16, 10);
+ /* IPI status vector */
+ vector = extract8(val, 0, 5);
+ cs = ipi_getcpu(cpuid);
+ if (cs == NULL) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /* override requester_id */
+ attrs.requester_id = cs->cpu_index;
+ loongson_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
+ break;
+ }
+
+ return MEMTX_OK;
+}
+
+static const MemoryRegionOps loongson_ipi_ops = {
+ .read_with_attrs = loongson_ipi_readl,
+ .write_with_attrs = loongson_ipi_writel,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* mail send and any send only support writeq */
+static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
+{
+ MemTxResult ret = MEMTX_OK;
+
+ addr &= 0xfff;
+ switch (addr) {
+ case MAIL_SEND_OFFSET:
+ ret = mail_send(val, attrs);
+ break;
+ case ANY_SEND_OFFSET:
+ ret = any_send(val, attrs);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const MemoryRegionOps loongson_ipi64_ops = {
+ .write_with_attrs = loongson_ipi_writeq,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void loongson_ipi_realize(DeviceState *dev, Error **errp)
+{
+ LoongsonIPI *s = LOONGSON_IPI(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ int i;
+
+ if (s->num_cpu == 0) {
+ error_setg(errp, "num-cpu must be at least 1");
+ return;
+ }
+
+ memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongson_ipi_ops,
+ s, "loongson_ipi_iocsr", 0x48);
+
+ /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
+ s->ipi_iocsr_mem.disable_reentrancy_guard = true;
+
+ sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
+
+ memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
+ &loongson_ipi64_ops,
+ s, "loongson_ipi64_iocsr", 0x118);
+ sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
+
+ s->cpu = g_new0(IPICore, s->num_cpu);
+ if (s->cpu == NULL) {
+ error_setg(errp, "Memory allocation for ExtIOICore faile");
+ return;
+ }
+
+ for (i = 0; i < s->num_cpu; i++) {
+ qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
+ }
+}
+
+static const VMStateDescription vmstate_ipi_core = {
+ .name = "ipi-single",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(status, IPICore),
+ VMSTATE_UINT32(en, IPICore),
+ VMSTATE_UINT32(set, IPICore),
+ VMSTATE_UINT32(clear, IPICore),
+ VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_loongson_ipi = {
+ .name = TYPE_LOONGSON_IPI,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPI, num_cpu,
+ vmstate_ipi_core, IPICore),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property ipi_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", LoongsonIPI, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void loongson_ipi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = loongson_ipi_realize;
+ device_class_set_props(dc, ipi_properties);
+ dc->vmsd = &vmstate_loongson_ipi;
+}
+
+static void loongson_ipi_finalize(Object *obj)
+{
+ LoongsonIPI *s = LOONGSON_IPI(obj);
+
+ g_free(s->cpu);
+}
+
+static const TypeInfo loongson_ipi_info = {
+ .name = TYPE_LOONGSON_IPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LoongsonIPI),
+ .class_init = loongson_ipi_class_init,
+ .instance_finalize = loongson_ipi_finalize,
+};
+
+static void loongson_ipi_register_types(void)
+{
+ type_register_static(&loongson_ipi_info);
+}
+
+type_init(loongson_ipi_register_types)
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index 58140da5f2..0d1b7d0a43 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -20,7 +20,7 @@ system_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
system_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c'))
system_ss.add(when: 'CONFIG_I8259', if_true: files('i8259_common.c', 'i8259.c'))
system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c'))
-system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
+system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'), if_false: files('ioapic-stub.c'))
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c'))
system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
@@ -68,7 +68,7 @@ specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c'))
specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
if_true: files('spapr_xive_kvm.c'))
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
-specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c'))
+specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))
diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c
index f4a848460b..6771645699 100644
--- a/hw/intc/s390_flic.c
+++ b/hw/intc/s390_flic.c
@@ -405,6 +405,8 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
static Property s390_flic_common_properties[] = {
DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState,
adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI),
+ DEFINE_PROP_BOOL("migration-enabled", S390FLICState,
+ migration_enabled, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -457,7 +459,9 @@ type_init(qemu_s390_flic_register_types)
static bool adapter_info_so_needed(void *opaque)
{
- return css_migration_enabled();
+ S390FLICState *fs = s390_get_flic();
+
+ return fs->migration_enabled;
}
const VMStateDescription vmstate_adapter_info_so = {
diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c
index baaa30dcb7..330f08dfdc 100644
--- a/hw/intc/s390_flic_kvm.c
+++ b/hw/intc/s390_flic_kvm.c
@@ -324,6 +324,34 @@ static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
return r ? -errno : 0;
}
+static int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
+{
+ struct kvm_irq_routing_entry kroute = {};
+ int virq;
+
+ if (!kvm_gsi_routing_enabled()) {
+ return -ENOSYS;
+ }
+
+ virq = kvm_irqchip_get_virq(s);
+ if (virq < 0) {
+ return virq;
+ }
+
+ kroute.gsi = virq;
+ kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER;
+ kroute.flags = 0;
+ kroute.u.adapter.summary_addr = adapter->summary_addr;
+ kroute.u.adapter.ind_addr = adapter->ind_addr;
+ kroute.u.adapter.summary_offset = adapter->summary_offset;
+ kroute.u.adapter.ind_offset = adapter->ind_offset;
+ kroute.u.adapter.adapter_id = adapter->adapter_id;
+
+ kvm_add_routing_entry(s, &kroute);
+
+ return virq;
+}
+
static int kvm_s390_add_adapter_routes(S390FLICState *fs,
AdapterRoutes *routes)
{
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 47340b5bc1..b815cea129 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -291,11 +291,9 @@ sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PR
sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx"
sh_intc_set(int id, int enable) "setting interrupt group %d to %d"
-# loongarch_ipi.c
-loongarch_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
-loongarch_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
-loongarch_ipi_unsupported_cpuid(const char *s, uint32_t cpuid) "%s unsupported cpuid 0x%" PRIx32
-
+# loongson_ipi.c
+loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
+loongson_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64
# loongarch_pch_pic.c
loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d"
loongarch_pch_pic_low_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
index 5727efed6d..90a0dba9d5 100644
--- a/hw/loongarch/Kconfig
+++ b/hw/loongarch/Kconfig
@@ -1,5 +1,8 @@
config LOONGARCH_VIRT
bool
+ default y
+ depends on LOONGARCH64 && FDT
+ select DEVICE_TREE
select PCI
select PCI_EXPRESS_GENERIC_BRIDGE
imply VIRTIO_VGA
@@ -8,7 +11,7 @@ config LOONGARCH_VIRT
select SERIAL
select VIRTIO_PCI
select PLATFORM_BUS
- select LOONGARCH_IPI
+ select LOONGSON_IPI
select LOONGARCH_PCH_PIC
select LOONGARCH_PCH_MSI
select LOONGARCH_EXTIOI
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
index e5ab1080af..5ef010d4da 100644
--- a/hw/loongarch/acpi-build.c
+++ b/hw/loongarch/acpi-build.c
@@ -105,14 +105,15 @@ build_facs(GArray *table_data)
/* build MADT */
static void
-build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams)
+build_madt(GArray *table_data, BIOSLinker *linker,
+ LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
MachineClass *mc = MACHINE_GET_CLASS(ms);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
int i, arch_id;
- AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id,
+ .oem_table_id = lvms->oem_table_id };
acpi_table_begin(&table, table_data);
@@ -167,11 +168,11 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
int i, arch_id, node_id;
uint64_t mem_len, mem_base;
int nb_numa_nodes = machine->numa_state->num_nodes;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine);
- AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id,
+ .oem_table_id = lvms->oem_table_id };
acpi_table_begin(&table, table_data);
build_append_int_noprefix(table_data, 1, 4); /* Reserved */
@@ -279,13 +280,13 @@ static void
build_la_ged_aml(Aml *dsdt, MachineState *machine)
{
uint32_t event;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
- HOTPLUG_HANDLER(lams->acpi_ged),
+ HOTPLUG_HANDLER(lvms->acpi_ged),
VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
VIRT_GED_EVT_ADDR);
- event = object_property_get_uint(OBJECT(lams->acpi_ged),
+ event = object_property_get_uint(OBJECT(lvms->acpi_ged),
"ged-event", &error_abort);
if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
@@ -295,7 +296,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine)
acpi_dsdt_add_power_button(dsdt);
}
-static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams)
+static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms)
{
struct GPEXConfig cfg = {
.mmio64.base = VIRT_PCI_MEM_BASE,
@@ -305,13 +306,13 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams)
.ecam.base = VIRT_PCI_CFG_BASE,
.ecam.size = VIRT_PCI_CFG_SIZE,
.irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS,
- .bus = lams->pci_bus,
+ .bus = lvms->pci_bus,
};
acpi_dsdt_add_gpex(scope, &cfg);
}
-static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
+static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms)
{
Aml *dev, *crs;
MemoryRegion *flash_mem;
@@ -322,11 +323,11 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
hwaddr flash1_base;
hwaddr flash1_size;
- flash_mem = pflash_cfi01_get_memory(lams->flash[0]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
flash0_base = flash_mem->addr;
flash0_size = memory_region_size(flash_mem);
- flash_mem = pflash_cfi01_get_memory(lams->flash[1]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
flash1_base = flash_mem->addr;
flash1_size = memory_region_size(flash_mem);
@@ -352,7 +353,7 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams)
}
#ifdef CONFIG_TPM
-static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms)
+static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms)
{
PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
@@ -391,18 +392,18 @@ static void
build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
{
Aml *dsdt, *scope, *pkg;
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
- AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id,
- .oem_table_id = lams->oem_table_id };
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
+ AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id,
+ .oem_table_id = lvms->oem_table_id };
acpi_table_begin(&table, table_data);
dsdt = init_aml_allocator();
build_uart_device_aml(dsdt);
- build_pci_device_aml(dsdt, lams);
+ build_pci_device_aml(dsdt, lvms);
build_la_ged_aml(dsdt, machine);
- build_flash_aml(dsdt, lams);
+ build_flash_aml(dsdt, lvms);
#ifdef CONFIG_TPM
- acpi_dsdt_add_tpm(dsdt, lams);
+ acpi_dsdt_add_tpm(dsdt, lvms);
#endif
/* System State Package */
scope = aml_scope("\\");
@@ -421,7 +422,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
GArray *table_offsets;
AcpiFadtData fadt_data;
unsigned facs, rsdt, dsdt;
@@ -455,14 +456,14 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
fadt_data.dsdt_tbl_offset = &dsdt;
fadt_data.xdsdt_tbl_offset = &dsdt;
build_fadt(tables_blob, tables->linker, &fadt_data,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
acpi_add_table(table_offsets, tables_blob);
- build_madt(tables_blob, tables->linker, lams);
+ build_madt(tables_blob, tables->linker, lvms);
acpi_add_table(table_offsets, tables_blob);
build_pptt(tables_blob, tables->linker, machine,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
acpi_add_table(table_offsets, tables_blob);
build_srat(tables_blob, tables->linker, machine);
@@ -470,13 +471,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
if (machine->numa_state->num_nodes) {
if (machine->numa_state->have_numa_distance) {
acpi_add_table(table_offsets, tables_blob);
- build_slit(tables_blob, tables->linker, machine, lams->oem_id,
- lams->oem_table_id);
+ build_slit(tables_blob, tables->linker, machine, lvms->oem_id,
+ lvms->oem_table_id);
}
if (machine->numa_state->hmat_enabled) {
acpi_add_table(table_offsets, tables_blob);
build_hmat(tables_blob, tables->linker, machine->numa_state,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
}
}
@@ -486,8 +487,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
.base = cpu_to_le64(VIRT_PCI_CFG_BASE),
.size = cpu_to_le64(VIRT_PCI_CFG_SIZE),
};
- build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id,
- lams->oem_table_id);
+ build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id,
+ lvms->oem_table_id);
}
#ifdef CONFIG_TPM
@@ -495,8 +496,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
acpi_add_table(table_offsets, tables_blob);
build_tpm2(tables_blob, tables->linker,
- tables->tcpalog, lams->oem_id,
- lams->oem_table_id);
+ tables->tcpalog, lvms->oem_id,
+ lvms->oem_table_id);
}
#endif
/* Add tables supplied by user (if any) */
@@ -510,13 +511,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine)
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
build_rsdt(tables_blob, tables->linker, table_offsets,
- lams->oem_id, lams->oem_table_id);
+ lvms->oem_id, lvms->oem_table_id);
/* RSDP is in FSEG memory, so allocate it separately */
{
AcpiRsdpData rsdp_data = {
.revision = 0,
- .oem_id = lams->oem_id,
+ .oem_id = lvms->oem_id,
.xsdt_tbl_offset = NULL,
.rsdt_tbl_offset = &rsdt,
};
@@ -593,17 +594,25 @@ static const VMStateDescription vmstate_acpi_build = {
},
};
-void loongarch_acpi_setup(LoongArchMachineState *lams)
+static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms)
+{
+ if (lvms->acpi == ON_OFF_AUTO_OFF) {
+ return false;
+ }
+ return true;
+}
+
+void loongarch_acpi_setup(LoongArchVirtMachineState *lvms)
{
AcpiBuildTables tables;
AcpiBuildState *build_state;
- if (!lams->fw_cfg) {
+ if (!lvms->fw_cfg) {
ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
return;
}
- if (!loongarch_is_acpi_enabled(lams)) {
+ if (!loongarch_is_acpi_enabled(lvms)) {
ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
return;
}
@@ -611,7 +620,7 @@ void loongarch_acpi_setup(LoongArchMachineState *lams)
build_state = g_malloc0(sizeof *build_state);
acpi_build_tables_init(&tables);
- acpi_build(&tables, MACHINE(lams));
+ acpi_build(&tables, MACHINE(lvms));
/* Now expose it all to Guest */
build_state->table_mr = acpi_add_rom_blob(acpi_build_update,
diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
new file mode 100644
index 0000000000..b8e1aa18d5
--- /dev/null
+++ b/hw/loongarch/boot.c
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * LoongArch boot helper functions.
+ *
+ * Copyright (c) 2023 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "target/loongarch/cpu.h"
+#include "hw/loongarch/virt.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "qemu/error-report.h"
+#include "sysemu/reset.h"
+#include "sysemu/qtest.h"
+
+struct memmap_entry *memmap_table;
+unsigned memmap_entries;
+
+ram_addr_t initrd_offset;
+uint64_t initrd_size;
+
+static const unsigned int slave_boot_code[] = {
+ /* Configure reset ebase. */
+ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */
+
+ /* Disable interrupt. */
+ 0x0380100c, /* ori $t0, $zero,0x4 */
+ 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */
+
+ /* Clear mailbox. */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+ 0x06481da0, /* iocsrwr.d $zero, $t1 */
+
+ /* Enable IPI interrupt. */
+ 0x1400002c, /* lu12i.w $t0, 1(0x1) */
+ 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */
+ 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */
+ 0x064819ac, /* iocsrwr.w $t0, $t1 */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+
+ /* Wait for wakeup <.L11>: */
+ 0x06488000, /* idle 0x0 */
+ 0x03400000, /* andi $zero, $zero, 0x0 */
+ 0x064809ac, /* iocsrrd.w $t0, $t1 */
+ 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */
+
+ /* Read and clear IPI interrupt. */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x064809ac, /* iocsrrd.w $t0, $t1 */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */
+ 0x064819ac, /* iocsrwr.w $t0, $t1 */
+
+ /* Disable IPI interrupt. */
+ 0x1400002c, /* lu12i.w $t0, 1(0x1) */
+ 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */
+
+ /* Read mail buf and jump to specified entry */
+ 0x1400002d, /* lu12i.w $t1, 1(0x1) */
+ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */
+ 0x06480dac, /* iocsrrd.d $t0, $t1 */
+ 0x00150181, /* move $ra, $t0 */
+ 0x4c000020, /* jirl $zero, $ra,0 */
+};
+
+static inline void *guidcpy(void *dst, const void *src)
+{
+ return memcpy(dst, src, sizeof(efi_guid_t));
+}
+
+static void init_efi_boot_memmap(struct efi_system_table *systab,
+ void *p, void *start)
+{
+ unsigned i;
+ struct efi_boot_memmap *boot_memmap = p;
+ efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID;
+
+ /* efi_configuration_table 1 */
+ guidcpy(&systab->tables[0].guid, &tbl_guid);
+ systab->tables[0].table = (struct efi_configuration_table *)(p - start);
+ systab->nr_tables = 1;
+
+ boot_memmap->desc_size = sizeof(efi_memory_desc_t);
+ boot_memmap->desc_ver = 1;
+ boot_memmap->map_size = 0;
+
+ efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap);
+ for (i = 0; i < memmap_entries; i++) {
+ map = (void *)boot_memmap + sizeof(*map);
+ map[i].type = memmap_table[i].type;
+ map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB);
+ map[i].num_pages = ROUND_DOWN(memmap_table[i].address +
+ memmap_table[i].length - map[i].phys_addr, 64 * KiB);
+ p += sizeof(efi_memory_desc_t);
+ }
+}
+
+static void init_efi_initrd_table(struct efi_system_table *systab,
+ void *p, void *start)
+{
+ efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
+ struct efi_initrd *initrd_table = p;
+
+ /* efi_configuration_table 2 */
+ guidcpy(&systab->tables[1].guid, &tbl_guid);
+ systab->tables[1].table = (struct efi_configuration_table *)(p - start);
+ systab->nr_tables = 2;
+
+ initrd_table->base = initrd_offset;
+ initrd_table->size = initrd_size;
+}
+
+static void init_efi_fdt_table(struct efi_system_table *systab)
+{
+ efi_guid_t tbl_guid = DEVICE_TREE_GUID;
+
+ /* efi_configuration_table 3 */
+ guidcpy(&systab->tables[2].guid, &tbl_guid);
+ systab->tables[2].table = (void *)FDT_BASE;
+ systab->nr_tables = 3;
+}
+
+static void init_systab(struct loongarch_boot_info *info, void *p, void *start)
+{
+ void *bp_tables_start;
+ struct efi_system_table *systab = p;
+
+ info->a2 = p - start;
+
+ systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE;
+ systab->hdr.revision = EFI_SPECIFICATION_VERSION;
+ systab->hdr.revision = sizeof(struct efi_system_table),
+ systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8;
+ systab->runtime = 0;
+ systab->boottime = 0;
+ systab->nr_tables = 0;
+
+ p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB);
+
+ systab->tables = p;
+ bp_tables_start = p;
+
+ init_efi_boot_memmap(systab, p, start);
+ p += ROUND_UP(sizeof(struct efi_boot_memmap) +
+ sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB);
+ init_efi_initrd_table(systab, p, start);
+ p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB);
+ init_efi_fdt_table(systab);
+
+ systab->tables = (struct efi_configuration_table *)(bp_tables_start - start);
+}
+
+static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start)
+{
+ hwaddr cmdline_addr = p - start;
+
+ info->a0 = 1;
+ info->a1 = cmdline_addr;
+
+ memcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE);
+}
+
+static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
+{
+ return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
+}
+
+static int64_t load_kernel_info(struct loongarch_boot_info *info)
+{
+ uint64_t kernel_entry, kernel_low, kernel_high;
+ ssize_t kernel_size;
+
+ kernel_size = load_elf(info->kernel_filename, NULL,
+ cpu_loongarch_virt_to_phys, NULL,
+ &kernel_entry, &kernel_low,
+ &kernel_high, NULL, 0,
+ EM_LOONGARCH, 1, 0);
+
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s': %s",
+ info->kernel_filename,
+ load_elf_strerror(kernel_size));
+ exit(1);
+ }
+
+ if (info->initrd_filename) {
+ initrd_size = get_image_size(info->initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);
+
+ if (initrd_offset + initrd_size > info->ram_size) {
+ error_report("memory too small for initial ram disk '%s'",
+ info->initrd_filename);
+ exit(1);
+ }
+
+ initrd_size = load_image_targphys(info->initrd_filename, initrd_offset,
+ info->ram_size - initrd_offset);
+ }
+
+ if (initrd_size == (target_ulong)-1) {
+ error_report("could not load initial ram disk '%s'",
+ info->initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+
+ return kernel_entry;
+}
+
+static void reset_load_elf(void *opaque)
+{
+ LoongArchCPU *cpu = opaque;
+ CPULoongArchState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+ if (env->load_elf) {
+ if (cpu == LOONGARCH_CPU(first_cpu)) {
+ env->gpr[4] = env->boot_info->a0;
+ env->gpr[5] = env->boot_info->a1;
+ env->gpr[6] = env->boot_info->a2;
+ }
+ cpu_set_pc(CPU(cpu), env->elf_address);
+ }
+}
+
+static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info,
+ FWCfgState *fw_cfg)
+{
+ /*
+ * Expose the kernel, the command line, and the initrd in fw_cfg.
+ * We don't process them here at all, it's all left to the
+ * firmware.
+ */
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
+ info->kernel_filename,
+ false);
+
+ if (info->initrd_filename) {
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
+ info->initrd_filename, false);
+ }
+
+ if (info->kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(info->kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
+ info->kernel_cmdline);
+ }
+}
+
+static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms,
+ struct loongarch_boot_info *info)
+{
+ fw_cfg_add_kernel_info(info, lvms->fw_cfg);
+}
+
+static void init_boot_rom(struct loongarch_boot_info *info, void *p)
+{
+ void *start = p;
+
+ init_cmdline(info, p, start);
+ p += COMMAND_LINE_SIZE;
+
+ init_systab(info, p, start);
+}
+
+static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info)
+{
+ void *p, *bp;
+ int64_t kernel_addr = 0;
+ LoongArchCPU *lacpu;
+ CPUState *cs;
+
+ if (info->kernel_filename) {
+ kernel_addr = load_kernel_info(info);
+ } else {
+ if(!qtest_enabled()) {
+ error_report("Need kernel filename\n");
+ exit(1);
+ }
+ }
+
+ /* Load cmdline and system tables at [0 - 1 MiB] */
+ p = g_malloc0(1 * MiB);
+ bp = p;
+ init_boot_rom(info, p);
+ rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory);
+
+ /* Load slave boot code at pflash0 . */
+ void *boot_code = g_malloc0(VIRT_FLASH0_SIZE);
+ memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code));
+ rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE);
+
+ CPU_FOREACH(cs) {
+ lacpu = LOONGARCH_CPU(cs);
+ lacpu->env.load_elf = true;
+ if (cs == first_cpu) {
+ lacpu->env.elf_address = kernel_addr;
+ } else {
+ lacpu->env.elf_address = VIRT_FLASH0_BASE;
+ }
+ lacpu->env.boot_info = info;
+ }
+
+ g_free(boot_code);
+ g_free(bp);
+}
+
+void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info)
+{
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms);
+ int i;
+
+ /* register reset function */
+ for (i = 0; i < ms->smp.cpus; i++) {
+ qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i)));
+ }
+
+ info->kernel_filename = ms->kernel_filename;
+ info->kernel_cmdline = ms->kernel_cmdline;
+ info->initrd_filename = ms->initrd_filename;
+
+ if (lvms->bios_loaded) {
+ loongarch_firmware_boot(lvms, info);
+ } else {
+ loongarch_direct_kernel_boot(info);
+ }
+}
diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c
index f15a17416c..35aeb2decb 100644
--- a/hw/loongarch/fw_cfg.c
+++ b/hw/loongarch/fw_cfg.c
@@ -17,7 +17,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device,
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms)
+FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms)
{
FWCfgState *fw_cfg;
int max_cpus = ms->smp.max_cpus;
diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h
index 7c0de4db4a..27ee68286e 100644
--- a/hw/loongarch/fw_cfg.h
+++ b/hw/loongarch/fw_cfg.h
@@ -11,5 +11,5 @@
#include "hw/boards.h"
#include "hw/nvram/fw_cfg.h"
-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms);
+FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms);
#endif
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
index c0421502ab..bce7ebac97 100644
--- a/hw/loongarch/meson.build
+++ b/hw/loongarch/meson.build
@@ -1,8 +1,9 @@
loongarch_ss = ss.source_set()
loongarch_ss.add(files(
'fw_cfg.c',
+ 'boot.c',
))
-loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt])
+loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c'))
loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c'))
hw_arch += {'loongarch': loongarch_ss}
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 441d764843..f0640d2d80 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -21,7 +21,7 @@
#include "net/net.h"
#include "hw/loader.h"
#include "elf.h"
-#include "hw/intc/loongarch_ipi.h"
+#include "hw/intc/loongson_ipi.h"
#include "hw/intc/loongarch_extioi.h"
#include "hw/intc/loongarch_pch_pic.h"
#include "hw/intc/loongarch_pch_msi.h"
@@ -46,15 +46,7 @@
#include "hw/block/flash.h"
#include "qemu/error-report.h"
-
-struct loaderparams {
- uint64_t ram_size;
- const char *kernel_filename;
- const char *kernel_cmdline;
- const char *initrd_filename;
-};
-
-static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams,
+static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms,
const char *name,
const char *alias_prop_name)
{
@@ -69,16 +61,16 @@ static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams,
qdev_prop_set_uint16(dev, "id2", 0x00);
qdev_prop_set_uint16(dev, "id3", 0x00);
qdev_prop_set_string(dev, "name", name);
- object_property_add_child(OBJECT(lams), name, OBJECT(dev));
- object_property_add_alias(OBJECT(lams), alias_prop_name,
+ object_property_add_child(OBJECT(lvms), name, OBJECT(dev));
+ object_property_add_alias(OBJECT(lvms), alias_prop_name,
OBJECT(dev), "drive");
return PFLASH_CFI01(dev);
}
-static void virt_flash_create(LoongArchMachineState *lams)
+static void virt_flash_create(LoongArchVirtMachineState *lvms)
{
- lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0");
- lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1");
+ lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0");
+ lvms->flash[1] = virt_flash_create1(lvms, "virt.flash1", "pflash1");
}
static void virt_flash_map1(PFlashCFI01 *flash,
@@ -104,19 +96,114 @@ static void virt_flash_map1(PFlashCFI01 *flash,
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
}
-static void virt_flash_map(LoongArchMachineState *lams,
+static void virt_flash_map(LoongArchVirtMachineState *lvms,
MemoryRegion *sysmem)
{
- PFlashCFI01 *flash0 = lams->flash[0];
- PFlashCFI01 *flash1 = lams->flash[1];
+ PFlashCFI01 *flash0 = lvms->flash[0];
+ PFlashCFI01 *flash1 = lvms->flash[1];
virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem);
virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem);
}
-static void fdt_add_flash_node(LoongArchMachineState *lams)
+static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+
+ *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/cpuic");
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,cpu-interrupt-controller");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ g_free(nodename);
+}
+
+static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *cpuintc_phandle,
+ uint32_t *eiointc_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr extioi_base = APIC_BASE;
+ hwaddr extioi_size = EXTIOI_SIZE;
+
+ *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls2k2000-eiointc");
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *cpuintc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0,
+ extioi_base, 0x0, extioi_size);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_pic_phandle)
+{
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_pic_base = VIRT_PCH_REG_BASE;
+ hwaddr pch_pic_size = VIRT_PCH_REG_SIZE;
+
+ *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-pic-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0,
+ pch_pic_base, 0, pch_pic_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0);
+ g_free(nodename);
+}
+
+static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms,
+ uint32_t *eiointc_phandle,
+ uint32_t *pch_msi_phandle)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
+ char *nodename;
+ hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW;
+ hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE;
+
+ *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base);
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,pch-msi-1.0");
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg",
+ 0, pch_msi_base,
+ 0, pch_msi_size);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *eiointc_phandle);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec",
+ VIRT_PCH_PIC_IRQ_NUM);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs",
+ EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM);
+ g_free(nodename);
+}
+
+static void fdt_add_flash_node(LoongArchVirtMachineState *lvms)
+{
+ MachineState *ms = MACHINE(lvms);
char *nodename;
MemoryRegion *flash_mem;
@@ -126,11 +213,11 @@ static void fdt_add_flash_node(LoongArchMachineState *lams)
hwaddr flash1_base;
hwaddr flash1_size;
- flash_mem = pflash_cfi01_get_memory(lams->flash[0]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[0]);
flash0_base = flash_mem->addr;
flash0_size = memory_region_size(flash_mem);
- flash_mem = pflash_cfi01_get_memory(lams->flash[1]);
+ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]);
flash1_base = flash_mem->addr;
flash1_size = memory_region_size(flash_mem);
@@ -144,26 +231,33 @@ static void fdt_add_flash_node(LoongArchMachineState *lams)
g_free(nodename);
}
-static void fdt_add_rtc_node(LoongArchMachineState *lams)
+static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle)
{
char *nodename;
hwaddr base = VIRT_RTC_REG_BASE;
hwaddr size = VIRT_RTC_LEN;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/rtc@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc");
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
+ "loongson,ls7a-rtc");
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
+ VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
g_free(nodename);
}
-static void fdt_add_uart_node(LoongArchMachineState *lams)
+static void fdt_add_uart_node(LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle)
{
char *nodename;
hwaddr base = VIRT_UART_BASE;
hwaddr size = VIRT_UART_SIZE;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/serial@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -171,14 +265,18 @@ static void fdt_add_uart_node(LoongArchMachineState *lams)
qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size);
qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000);
qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
+ VIRT_UART_IRQ - VIRT_GSI_BASE, 0x4);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
+ *pch_pic_phandle);
g_free(nodename);
}
-static void create_fdt(LoongArchMachineState *lams)
+static void create_fdt(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
- ms->fdt = create_device_tree(&lams->fdt_size);
+ ms->fdt = create_device_tree(&lvms->fdt_size);
if (!ms->fdt) {
error_report("create_device_tree() failed");
exit(1);
@@ -192,10 +290,10 @@ static void create_fdt(LoongArchMachineState *lams)
qemu_fdt_add_subnode(ms->fdt, "/chosen");
}
-static void fdt_add_cpu_nodes(const LoongArchMachineState *lams)
+static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms)
{
int num;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
int smp_cpus = ms->smp.cpus;
qemu_fdt_add_subnode(ms->fdt, "/cpus");
@@ -249,11 +347,11 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams)
}
}
-static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams)
+static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms)
{
char *nodename;
hwaddr base = VIRT_FWCFG_BASE;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -265,7 +363,62 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams)
g_free(nodename);
}
-static void fdt_add_pcie_node(const LoongArchMachineState *lams)
+static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms,
+ char *nodename,
+ uint32_t *pch_pic_phandle)
+{
+ int pin, dev;
+ uint32_t irq_map_stride = 0;
+ uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {};
+ uint32_t *irq_map = full_irq_map;
+ const MachineState *ms = MACHINE(lvms);
+
+ /* This code creates a standard swizzle of interrupts such that
+ * each device's first interrupt is based on it's PCI_SLOT number.
+ * (See pci_swizzle_map_irq_fn())
+ *
+ * We only need one entry per interrupt in the table (not one per
+ * possible slot) seeing the interrupt-map-mask will allow the table
+ * to wrap to any number of devices.
+ */
+
+ for (dev = 0; dev < GPEX_NUM_IRQS; dev++) {
+ int devfn = dev * 0x8;
+
+ for (pin = 0; pin < GPEX_NUM_IRQS; pin++) {
+ int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS);
+ int i = 0;
+
+ /* Fill PCI address cells */
+ irq_map[i] = cpu_to_be32(devfn << 8);
+ i += 3;
+
+ /* Fill PCI Interrupt cells */
+ irq_map[i] = cpu_to_be32(pin + 1);
+ i += 1;
+
+ /* Fill interrupt controller phandle and cells */
+ irq_map[i++] = cpu_to_be32(*pch_pic_phandle);
+ irq_map[i++] = cpu_to_be32(irq_nr);
+
+ if (!irq_map_stride) {
+ irq_map_stride = i;
+ }
+ irq_map += irq_map_stride;
+ }
+ }
+
+
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map,
+ GPEX_NUM_IRQS * GPEX_NUM_IRQS *
+ irq_map_stride * sizeof(uint32_t));
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask",
+ 0x1800, 0, 0, 0x7);
+}
+
+static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle,
+ uint32_t *pch_msi_phandle)
{
char *nodename;
hwaddr base_mmio = VIRT_PCI_MEM_BASE;
@@ -276,7 +429,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams)
hwaddr size_pcie = VIRT_PCI_CFG_SIZE;
hwaddr base = base_pcie;
- const MachineState *ms = MACHINE(lams);
+ const MachineState *ms = MACHINE(lvms);
nodename = g_strdup_printf("/pcie@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
@@ -296,34 +449,11 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams)
2, base_pio, 2, size_pio,
1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
2, base_mmio, 2, size_mmio);
- g_free(nodename);
-}
-
-static void fdt_add_irqchip_node(LoongArchMachineState *lams)
-{
- MachineState *ms = MACHINE(lams);
- char *nodename;
- uint32_t irqchip_phandle;
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map",
+ 0, *pch_msi_phandle, 0, 0x10000);
- irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt);
- qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle);
+ fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle);
- nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE);
- qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3);
- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2);
- qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2);
- qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0);
-
- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
- "loongarch,ls7a");
-
- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
- 2, VIRT_IOAPIC_REG_BASE,
- 2, PCH_PIC_ROUTE_ENTRY_OFFSET);
-
- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle);
g_free(nodename);
}
@@ -333,7 +463,7 @@ static void fdt_add_memory_node(MachineState *ms,
char *nodename = g_strdup_printf("/memory@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, nodename);
- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
+ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, 0, size);
qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory");
if (ms->numa_state && ms->numa_state->num_nodes) {
@@ -343,15 +473,15 @@ static void fdt_add_memory_node(MachineState *ms,
g_free(nodename);
}
-static void virt_build_smbios(LoongArchMachineState *lams)
+static void virt_build_smbios(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ MachineState *ms = MACHINE(lvms);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
const char *product = "QEMU Virtual Machine";
- if (!lams->fw_cfg) {
+ if (!lvms->fw_cfg) {
return;
}
@@ -363,39 +493,29 @@ static void virt_build_smbios(LoongArchMachineState *lams)
&smbios_anchor, &smbios_anchor_len, &error_fatal);
if (smbios_anchor) {
- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables",
+ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-tables",
smbios_tables, smbios_tables_len);
- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor",
+ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-anchor",
smbios_anchor, smbios_anchor_len);
}
}
-static void virt_machine_done(Notifier *notifier, void *data)
+static void virt_done(Notifier *notifier, void *data)
{
- LoongArchMachineState *lams = container_of(notifier,
- LoongArchMachineState, machine_done);
- virt_build_smbios(lams);
- loongarch_acpi_setup(lams);
+ LoongArchVirtMachineState *lvms = container_of(notifier,
+ LoongArchVirtMachineState, machine_done);
+ virt_build_smbios(lvms);
+ loongarch_acpi_setup(lvms);
}
static void virt_powerdown_req(Notifier *notifier, void *opaque)
{
- LoongArchMachineState *s = container_of(notifier,
- LoongArchMachineState, powerdown_notifier);
+ LoongArchVirtMachineState *s;
+ s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier);
acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS);
}
-struct memmap_entry {
- uint64_t address;
- uint64_t length;
- uint32_t type;
- uint32_t reserved;
-};
-
-static struct memmap_entry *memmap_table;
-static unsigned memmap_entries;
-
static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
{
/* Ensure there are no duplicate entries. */
@@ -412,35 +532,11 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
memmap_entries++;
}
-static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
-{
- return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS);
-}
-
-static int64_t load_kernel_info(const struct loaderparams *loaderparams)
-{
- uint64_t kernel_entry, kernel_low, kernel_high;
- ssize_t kernel_size;
-
- kernel_size = load_elf(loaderparams->kernel_filename, NULL,
- cpu_loongarch_virt_to_phys, NULL,
- &kernel_entry, &kernel_low,
- &kernel_high, NULL, 0,
- EM_LOONGARCH, 1, 0);
-
- if (kernel_size < 0) {
- error_report("could not load kernel '%s': %s",
- loaderparams->kernel_filename,
- load_elf_strerror(kernel_size));
- exit(1);
- }
- return kernel_entry;
-}
-
-static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams)
+static DeviceState *create_acpi_ged(DeviceState *pch_pic,
+ LoongArchVirtMachineState *lvms)
{
DeviceState *dev;
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
uint32_t event = ACPI_GED_PWR_DOWN_EVT;
if (ms->ram_slots) {
@@ -487,9 +583,12 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic)
return dev;
}
-static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams)
+static void virt_devices_init(DeviceState *pch_pic,
+ LoongArchVirtMachineState *lvms,
+ uint32_t *pch_pic_phandle,
+ uint32_t *pch_msi_phandle)
{
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
DeviceState *gpex_dev;
SysBusDevice *d;
PCIBus *pci_bus;
@@ -501,7 +600,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
d = SYS_BUS_DEVICE(gpex_dev);
sysbus_realize_and_unref(d, &error_fatal);
pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus;
- lams->pci_bus = pci_bus;
+ lvms->pci_bus = pci_bus;
/* Map only part size_ecam bytes of ECAM space */
ecam_alias = g_new0(MemoryRegion, 1);
@@ -533,11 +632,14 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i);
}
+ /* Add pcie node */
+ fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle);
+
serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0,
qdev_get_gpio_in(pch_pic,
VIRT_UART_IRQ - VIRT_GSI_BASE),
115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
- fdt_add_uart_node(lams);
+ fdt_add_uart_node(lvms, pch_pic_phandle);
/* Network init */
pci_init_nic_devices(pci_bus, mc->default_nic);
@@ -550,17 +652,17 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *
sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE,
qdev_get_gpio_in(pch_pic,
VIRT_RTC_IRQ - VIRT_GSI_BASE));
- fdt_add_rtc_node(lams);
+ fdt_add_rtc_node(lvms, pch_pic_phandle);
/* acpi ged */
- lams->acpi_ged = create_acpi_ged(pch_pic, lams);
+ lvms->acpi_ged = create_acpi_ged(pch_pic, lvms);
/* platform bus */
- lams->platform_bus_dev = create_platform_bus(pch_pic);
+ lvms->platform_bus_dev = create_platform_bus(pch_pic);
}
-static void loongarch_irq_init(LoongArchMachineState *lams)
+static void virt_irq_init(LoongArchVirtMachineState *lvms)
{
- MachineState *ms = MACHINE(lams);
+ MachineState *ms = MACHINE(lvms);
DeviceState *pch_pic, *pch_msi, *cpudev;
DeviceState *ipi, *extioi;
SysBusDevice *d;
@@ -568,6 +670,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
CPULoongArchState *env;
CPUState *cpu_state;
int cpu, pin, i, start, num;
+ uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle;
/*
* The connection of interrupts:
@@ -592,22 +695,25 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
*/
/* Create IPI device */
- ipi = qdev_new(TYPE_LOONGARCH_IPI);
+ ipi = qdev_new(TYPE_LOONGSON_IPI);
qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus);
sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal);
/* IPI iocsr memory region */
- memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX,
+ memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX,
sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0));
- memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR,
+ memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR,
sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1));
+ /* Add cpu interrupt-controller */
+ fdt_add_cpuic_node(lvms, &cpuintc_phandle);
+
for (cpu = 0; cpu < ms->smp.cpus; cpu++) {
cpu_state = qemu_get_cpu(cpu);
cpudev = DEVICE(cpu_state);
lacpu = LOONGARCH_CPU(cpu_state);
env = &(lacpu->env);
- env->address_space_iocsr = &lams->as_iocsr;
+ env->address_space_iocsr = &lvms->as_iocsr;
/* connect ipi irq to cpu irq */
qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI));
@@ -618,7 +724,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
extioi = qdev_new(TYPE_LOONGARCH_EXTIOI);
qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus);
sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal);
- memory_region_add_subregion(&lams->system_iocsr, APIC_BASE,
+ memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE,
sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0));
/*
@@ -633,6 +739,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
}
}
+ /* Add Extend I/O Interrupt Controller node */
+ fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle);
+
pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC);
num = VIRT_PCH_PIC_IRQ_NUM;
qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num);
@@ -652,6 +761,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i));
}
+ /* Add PCH PIC node */
+ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle);
+
pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI);
start = num;
num = EXTIOI_IRQS - start;
@@ -666,28 +778,31 @@ static void loongarch_irq_init(LoongArchMachineState *lams)
qdev_get_gpio_in(extioi, i + start));
}
- loongarch_devices_init(pch_pic, lams);
+ /* Add PCH MSI node */
+ fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle);
+
+ virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle);
}
-static void loongarch_firmware_init(LoongArchMachineState *lams)
+static void virt_firmware_init(LoongArchVirtMachineState *lvms)
{
- char *filename = MACHINE(lams)->firmware;
+ char *filename = MACHINE(lvms)->firmware;
char *bios_name = NULL;
int bios_size, i;
BlockBackend *pflash_blk0;
MemoryRegion *mr;
- lams->bios_loaded = false;
+ lvms->bios_loaded = false;
/* Map legacy -drive if=pflash to machine properties */
- for (i = 0; i < ARRAY_SIZE(lams->flash); i++) {
- pflash_cfi01_legacy_drive(lams->flash[i],
+ for (i = 0; i < ARRAY_SIZE(lvms->flash); i++) {
+ pflash_cfi01_legacy_drive(lvms->flash[i],
drive_get(IF_PFLASH, 0, i));
}
- virt_flash_map(lams, get_system_memory());
+ virt_flash_map(lvms, get_system_memory());
- pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]);
+ pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]);
if (pflash_blk0) {
if (filename) {
@@ -695,7 +810,7 @@ static void loongarch_firmware_init(LoongArchMachineState *lams)
"options at once");
exit(1);
}
- lams->bios_loaded = true;
+ lvms->bios_loaded = true;
return;
}
@@ -706,85 +821,24 @@ static void loongarch_firmware_init(LoongArchMachineState *lams)
exit(1);
}
- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0);
+ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lvms->flash[0]), 0);
bios_size = load_image_mr(bios_name, mr);
if (bios_size < 0) {
error_report("Could not load ROM image '%s'", bios_name);
exit(1);
}
g_free(bios_name);
- lams->bios_loaded = true;
- }
-}
-
-static void reset_load_elf(void *opaque)
-{
- LoongArchCPU *cpu = opaque;
- CPULoongArchState *env = &cpu->env;
-
- cpu_reset(CPU(cpu));
- if (env->load_elf) {
- cpu_set_pc(CPU(cpu), env->elf_address);
- }
-}
-
-static void fw_cfg_add_kernel_info(const struct loaderparams *loaderparams,
- FWCfgState *fw_cfg)
-{
- /*
- * Expose the kernel, the command line, and the initrd in fw_cfg.
- * We don't process them here at all, it's all left to the
- * firmware.
- */
- load_image_to_fw_cfg(fw_cfg,
- FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
- loaderparams->kernel_filename,
- false);
-
- if (loaderparams->initrd_filename) {
- load_image_to_fw_cfg(fw_cfg,
- FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
- loaderparams->initrd_filename, false);
- }
-
- if (loaderparams->kernel_cmdline) {
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
- strlen(loaderparams->kernel_cmdline) + 1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
- loaderparams->kernel_cmdline);
+ lvms->bios_loaded = true;
}
}
-static void loongarch_firmware_boot(LoongArchMachineState *lams,
- const struct loaderparams *loaderparams)
-{
- fw_cfg_add_kernel_info(loaderparams, lams->fw_cfg);
-}
-static void loongarch_direct_kernel_boot(LoongArchMachineState *lams,
- const struct loaderparams *loaderparams)
+static void virt_iocsr_misc_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
{
- MachineState *machine = MACHINE(lams);
- int64_t kernel_addr = 0;
- LoongArchCPU *lacpu;
- int i;
-
- kernel_addr = load_kernel_info(loaderparams);
- if (!machine->firmware) {
- for (i = 0; i < machine->smp.cpus; i++) {
- lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
- lacpu->env.load_elf = true;
- lacpu->env.elf_address = kernel_addr;
- }
- }
}
-static void loongarch_qemu_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
-}
-
-static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
+static uint64_t virt_iocsr_misc_read(void *opaque, hwaddr addr, unsigned size)
{
switch (addr) {
case VERSION_REG:
@@ -802,9 +856,9 @@ static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
return 0ULL;
}
-static const MemoryRegionOps loongarch_qemu_ops = {
- .read = loongarch_qemu_read,
- .write = loongarch_qemu_write,
+static const MemoryRegionOps virt_iocsr_misc_ops = {
+ .read = virt_iocsr_misc_read,
+ .write = virt_iocsr_misc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@@ -816,7 +870,7 @@ static const MemoryRegionOps loongarch_qemu_ops = {
},
};
-static void loongarch_init(MachineState *machine)
+static void virt_init(MachineState *machine)
{
LoongArchCPU *lacpu;
const char *cpu_model = machine->cpu_type;
@@ -824,16 +878,13 @@ static void loongarch_init(MachineState *machine)
ram_addr_t ram_size = machine->ram_size;
uint64_t highram_size = 0, phyAddr = 0;
MemoryRegion *address_space_mem = get_system_memory();
- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine);
int nb_numa_nodes = machine->numa_state->num_nodes;
NodeInfo *numa_info = machine->numa_state->nodes;
int i;
- hwaddr fdt_base;
const CPUArchIdList *possible_cpus;
MachineClass *mc = MACHINE_GET_CLASS(machine);
CPUState *cpu;
- char *ramName = NULL;
- struct loaderparams loaderparams = { };
if (!cpu_model) {
cpu_model = LOONGARCH_CPU_TYPE_NAME("la464");
@@ -843,16 +894,16 @@ static void loongarch_init(MachineState *machine)
error_report("ram_size must be greater than 1G.");
exit(1);
}
- create_fdt(lams);
+ create_fdt(lvms);
/* Create IOCSR space */
- memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL,
+ memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL,
machine, "iocsr", UINT64_MAX);
- address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR");
- memory_region_init_io(&lams->iocsr_mem, OBJECT(machine),
- &loongarch_qemu_ops,
+ address_space_init(&lvms->as_iocsr, &lvms->system_iocsr, "IOCSR");
+ memory_region_init_io(&lvms->iocsr_mem, OBJECT(machine),
+ &virt_iocsr_misc_ops,
machine, "iocsr_misc", 0x428);
- memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem);
+ memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem);
/* Init CPUs */
possible_cpus = mc->possible_cpu_arch_ids(machine);
@@ -863,14 +914,14 @@ static void loongarch_init(MachineState *machine)
lacpu = LOONGARCH_CPU(cpu);
lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id;
}
- fdt_add_cpu_nodes(lams);
+ fdt_add_cpu_nodes(lvms);
/* Node0 memory */
memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1);
fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0);
- memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram",
+ memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.node0.lowram",
machine->ram, offset, VIRT_LOWMEM_SIZE);
- memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem);
+ memory_region_add_subregion(address_space_mem, phyAddr, &lvms->lowmem);
offset += VIRT_LOWMEM_SIZE;
if (nb_numa_nodes > 0) {
@@ -882,9 +933,9 @@ static void loongarch_init(MachineState *machine)
phyAddr = VIRT_HIGHMEM_BASE;
memmap_add_entry(phyAddr, highram_size, 1);
fdt_add_memory_node(machine, phyAddr, highram_size, 0);
- memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram",
+ memory_region_init_alias(&lvms->highmem, NULL, "loongarch.node0.highram",
machine->ram, offset, highram_size);
- memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem);
+ memory_region_add_subregion(address_space_mem, phyAddr, &lvms->highmem);
/* Node1 - Nodemax memory */
offset += highram_size;
@@ -892,7 +943,7 @@ static void loongarch_init(MachineState *machine)
for (i = 1; i < nb_numa_nodes; i++) {
MemoryRegion *nodemem = g_new(MemoryRegion, 1);
- ramName = g_strdup_printf("loongarch.node%d.ram", i);
+ g_autofree char *ramName = g_strdup_printf("loongarch.node%d.ram", i);
memory_region_init_alias(nodemem, NULL, ramName, machine->ram,
offset, numa_info[i].node_mem);
memory_region_add_subregion(address_space_mem, phyAddr, nodemem);
@@ -925,49 +976,31 @@ static void loongarch_init(MachineState *machine)
}
/* load the BIOS image. */
- loongarch_firmware_init(lams);
+ virt_firmware_init(lvms);
/* fw_cfg init */
- lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine);
- rom_set_fw(lams->fw_cfg);
- if (lams->fw_cfg != NULL) {
- fw_cfg_add_file(lams->fw_cfg, "etc/memmap",
+ lvms->fw_cfg = virt_fw_cfg_init(ram_size, machine);
+ rom_set_fw(lvms->fw_cfg);
+ if (lvms->fw_cfg != NULL) {
+ fw_cfg_add_file(lvms->fw_cfg, "etc/memmap",
memmap_table,
sizeof(struct memmap_entry) * (memmap_entries));
}
- fdt_add_fw_cfg_node(lams);
- loaderparams.ram_size = ram_size;
- loaderparams.kernel_filename = machine->kernel_filename;
- loaderparams.kernel_cmdline = machine->kernel_cmdline;
- loaderparams.initrd_filename = machine->initrd_filename;
- /* load the kernel. */
- if (loaderparams.kernel_filename) {
- if (lams->bios_loaded) {
- loongarch_firmware_boot(lams, &loaderparams);
- } else {
- loongarch_direct_kernel_boot(lams, &loaderparams);
- }
- }
- fdt_add_flash_node(lams);
- /* register reset function */
- for (i = 0; i < machine->smp.cpus; i++) {
- lacpu = LOONGARCH_CPU(qemu_get_cpu(i));
- qemu_register_reset(reset_load_elf, lacpu);
- }
+ fdt_add_fw_cfg_node(lvms);
+ fdt_add_flash_node(lvms);
+
/* Initialize the IO interrupt subsystem */
- loongarch_irq_init(lams);
- fdt_add_irqchip_node(lams);
- platform_bus_add_all_fdt_nodes(machine->fdt, "/intc",
+ virt_irq_init(lvms);
+ platform_bus_add_all_fdt_nodes(machine->fdt, "/platic",
VIRT_PLATFORM_BUS_BASEADDRESS,
VIRT_PLATFORM_BUS_SIZE,
VIRT_PLATFORM_BUS_IRQ);
- lams->machine_done.notify = virt_machine_done;
- qemu_add_machine_init_done_notifier(&lams->machine_done);
+ lvms->machine_done.notify = virt_done;
+ qemu_add_machine_init_done_notifier(&lvms->machine_done);
/* connect powerdown request */
- lams->powerdown_notifier.notify = virt_powerdown_req;
- qemu_register_powerdown_notifier(&lams->powerdown_notifier);
+ lvms->powerdown_notifier.notify = virt_powerdown_req;
+ qemu_register_powerdown_notifier(&lvms->powerdown_notifier);
- fdt_add_pcie_node(lams);
/*
* Since lowmem region starts from 0 and Linux kernel legacy start address
* at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer
@@ -975,44 +1008,41 @@ static void loongarch_init(MachineState *machine)
* Put the FDT into the memory map as a ROM image: this will ensure
* the FDT is copied again upon reset, even if addr points into RAM.
*/
- fdt_base = 1 * MiB;
- qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size);
- rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base);
+ qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size);
+ rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE,
+ &address_space_memory);
+ qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
+ rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size));
+
+ lvms->bootinfo.ram_size = ram_size;
+ loongarch_load_kernel(machine, &lvms->bootinfo);
}
-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams)
+static void virt_get_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
- if (lams->acpi == ON_OFF_AUTO_OFF) {
- return false;
- }
- return true;
-}
-
-static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
- OnOffAuto acpi = lams->acpi;
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
+ OnOffAuto acpi = lvms->acpi;
visit_type_OnOffAuto(v, name, &acpi, errp);
}
-static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name,
+static void virt_set_acpi(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
- visit_type_OnOffAuto(v, name, &lams->acpi, errp);
+ visit_type_OnOffAuto(v, name, &lvms->acpi, errp);
}
-static void loongarch_machine_initfn(Object *obj)
+static void virt_initfn(Object *obj)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj);
- lams->acpi = ON_OFF_AUTO_AUTO;
- lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
- lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
- virt_flash_create(lams);
+ lvms->acpi = ON_OFF_AUTO_AUTO;
+ lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6);
+ lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
+ virt_flash_create(lvms);
}
static bool memhp_type_supported(DeviceState *dev)
@@ -1028,7 +1058,7 @@ static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
}
-static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev,
+static void virt_device_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
@@ -1039,14 +1069,14 @@ static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev,
static void virt_mem_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
/* the acpi ged is always exist */
- hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev,
+ hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev,
errp);
}
-static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev,
+static void virt_device_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
@@ -1057,14 +1087,14 @@ static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev,
static void virt_mem_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
- hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp);
- pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams));
+ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp);
+ pc_dimm_unplug(PC_DIMM(dev), MACHINE(lvms));
qdev_unrealize(dev);
}
-static void virt_machine_device_unplug(HotplugHandler *hotplug_dev,
+static void virt_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
@@ -1075,31 +1105,32 @@ static void virt_machine_device_unplug(HotplugHandler *hotplug_dev,
static void virt_mem_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
- pc_dimm_plug(PC_DIMM(dev), MACHINE(lams));
- hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged),
+ pc_dimm_plug(PC_DIMM(dev), MACHINE(lvms));
+ hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged),
dev, &error_abort);
}
-static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev,
+static void virt_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
- MachineClass *mc = MACHINE_GET_CLASS(lams);
+ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev);
+ MachineClass *mc = MACHINE_GET_CLASS(lvms);
+ PlatformBusDevice *pbus;
if (device_is_dynamic_sysbus(mc, dev)) {
- if (lams->platform_bus_dev) {
- platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev),
- SYS_BUS_DEVICE(dev));
+ if (lvms->platform_bus_dev) {
+ pbus = PLATFORM_BUS_DEVICE(lvms->platform_bus_dev);
+ platform_bus_link_device(pbus, SYS_BUS_DEVICE(dev));
}
} else if (memhp_type_supported(dev)) {
virt_mem_plug(hotplug_dev, dev, errp);
}
}
-static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
- DeviceState *dev)
+static HotplugHandler *virt_get_hotplug_handler(MachineState *machine,
+ DeviceState *dev)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
@@ -1139,8 +1170,8 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
return ms->possible_cpus;
}
-static CpuInstanceProperties
-virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms,
+ unsigned cpu_index)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
@@ -1151,24 +1182,22 @@ virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx)
{
- int64_t nidx = 0;
+ int64_t socket_id;
if (ms->numa_state->num_nodes) {
- nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes);
- if (ms->numa_state->num_nodes <= nidx) {
- nidx = ms->numa_state->num_nodes - 1;
- }
+ socket_id = ms->possible_cpus->cpus[idx].props.socket_id;
+ return socket_id % ms->numa_state->num_nodes;
+ } else {
+ return 0;
}
- return nidx;
}
-static void loongarch_class_init(ObjectClass *oc, void *data)
+static void virt_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
- mc->desc = "Loongson-3A5000 LS7A1000 machine";
- mc->init = loongarch_init;
+ mc->init = virt_init;
mc->default_ram_size = 1 * GiB;
mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464");
mc->default_ram_id = "loongarch.ram";
@@ -1184,15 +1213,15 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
mc->numa_mem_supported = true;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
- mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+ mc->get_hotplug_handler = virt_get_hotplug_handler;
mc->default_nic = "virtio-net-pci";
- hc->plug = loongarch_machine_device_plug_cb;
- hc->pre_plug = virt_machine_device_pre_plug;
- hc->unplug_request = virt_machine_device_unplug_request;
- hc->unplug = virt_machine_device_unplug;
+ hc->plug = virt_device_plug_cb;
+ hc->pre_plug = virt_device_pre_plug;
+ hc->unplug_request = virt_device_unplug_request;
+ hc->unplug = virt_device_unplug;
object_class_property_add(oc, "acpi", "OnOffAuto",
- loongarch_get_acpi, loongarch_set_acpi,
+ virt_get_acpi, virt_set_acpi,
NULL, NULL);
object_class_property_set_description(oc, "acpi",
"Enable ACPI");
@@ -1202,13 +1231,13 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
#endif
}
-static const TypeInfo loongarch_machine_types[] = {
+static const TypeInfo virt_machine_types[] = {
{
- .name = TYPE_LOONGARCH_MACHINE,
+ .name = TYPE_LOONGARCH_VIRT_MACHINE,
.parent = TYPE_MACHINE,
- .instance_size = sizeof(LoongArchMachineState),
- .class_init = loongarch_class_init,
- .instance_init = loongarch_machine_initfn,
+ .instance_size = sizeof(LoongArchVirtMachineState),
+ .class_init = virt_class_init,
+ .instance_init = virt_initfn,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
@@ -1216,4 +1245,4 @@ static const TypeInfo loongarch_machine_types[] = {
}
};
-DEFINE_TYPES(loongarch_machine_types)
+DEFINE_TYPES(virt_machine_types)
diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
index d88741ec9d..0092cda4e9 100644
--- a/hw/m68k/Kconfig
+++ b/hw/m68k/Kconfig
@@ -1,20 +1,28 @@
config AN5206
bool
+ default y
+ depends on M68K
select COLDFIRE
select PTIMER
config MCF5208
bool
+ default y
+ depends on M68K
select COLDFIRE
select PTIMER
config NEXTCUBE
bool
+ default y
+ depends on M68K
select FRAMEBUFFER
select ESCC
config Q800
bool
+ default y
+ depends on M68K
select MAC_VIA
select NUBUS
select MACFB
@@ -29,6 +37,8 @@ config Q800
config M68K_VIRT
bool
+ default y
+ depends on M68K
select M68K_IRQC
select VIRT_CTRL
select GOLDFISH_PIC
diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig
index e2697ced9c..d78ba843fa 100644
--- a/hw/microblaze/Kconfig
+++ b/hw/microblaze/Kconfig
@@ -1,5 +1,7 @@
config PETALOGIX_S3ADSP1800
bool
+ default y
+ depends on MICROBLAZE
select PFLASH_CFI01
select XILINX
select XILINX_AXI
@@ -8,6 +10,8 @@ config PETALOGIX_S3ADSP1800
config PETALOGIX_ML605
bool
+ default y
+ depends on MICROBLAZE
select PFLASH_CFI01
select SERIAL
select SSI_M25P80
@@ -18,4 +22,6 @@ config PETALOGIX_ML605
config XLNX_ZYNQMP_PMU
bool
+ default y
+ depends on MICROBLAZE
select XLNX_ZYNQMP
diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig
index 5c83ef49cf..a7f26edebe 100644
--- a/hw/mips/Kconfig
+++ b/hw/mips/Kconfig
@@ -1,5 +1,7 @@
config MALTA
bool
+ default y
+ depends on MIPS
imply PCNET_PCI
imply PCI_DEVICES
imply TEST_DEVICES
@@ -13,11 +15,15 @@ config MALTA
config MIPSSIM
bool
+ default y
+ depends on MIPS
select SERIAL
select MIPSNET
config JAZZ
bool
+ default y
+ depends on MIPS64
select ISA_BUS
select RC4030
select I8259
@@ -38,6 +44,8 @@ config JAZZ
config FULOONG
bool
+ default y
+ depends on MIPS64 && !TARGET_BIG_ENDIAN
imply PCI_DEVICES
imply TEST_DEVICES
imply ATI_VGA
@@ -48,6 +56,8 @@ config FULOONG
config LOONGSON3V
bool
+ default y
+ depends on MIPS64 && !TARGET_BIG_ENDIAN
imply PCI_DEVICES
imply TEST_DEVICES
imply VIRTIO_PCI
@@ -69,8 +79,11 @@ config MIPS_CPS
config MIPS_BOSTON
bool
+ default y
+ depends on MIPS64 && !TARGET_BIG_ENDIAN && FDT
imply PCI_DEVICES
imply TEST_DEVICES
+ select DEVICE_TREE
select FITLOADER
select MIPS_CPS
select PCI_EXPRESS_XILINX
diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c
index f99af22932..03a10b63c1 100644
--- a/hw/mips/loongson3_bootp.c
+++ b/hw/mips/loongson3_bootp.c
@@ -148,4 +148,5 @@ void init_reset_system(struct efi_reset_system_t *reset)
reset->Shutdown = cpu_to_le64(0xffffffffbfc000a8);
reset->ResetCold = cpu_to_le64(0xffffffffbfc00080);
reset->ResetWarm = cpu_to_le64(0xffffffffbfc00080);
+ reset->DoSuspend = cpu_to_le64(0xffffffffbfc000d0);
}
diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c
index b10a611a98..440268a074 100644
--- a/hw/mips/loongson3_virt.c
+++ b/hw/mips/loongson3_virt.c
@@ -127,6 +127,9 @@ static void loongson3_pm_write(void *opaque, hwaddr addr,
case 0x00:
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
return;
+ case 0x01:
+ qemu_system_suspend_request();
+ return;
case 0xff:
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
return;
@@ -250,6 +253,17 @@ static void init_boot_rom(void)
0x240D00FF, /* li t1, 0xff */
0xA18D0000, /* sb t1, (t0) */
0x1000FFFF, /* 1: b 1b */
+ 0x00000000, /* nop */
+ /* Suspend */
+ 0x3C0C9000, /* dli t0, 0x9000000010080010 */
+ 0x358C0000,
+ 0x000C6438,
+ 0x358C1008,
+ 0x000C6438,
+ 0x358C0010,
+ 0x240D0001, /* li t1, 0x01 */
+ 0xA18D0000, /* sb t1, (t0) */
+ 0x03e00008, /* jr ra */
0x00000000 /* nop */
};
@@ -265,6 +279,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device,
static void fw_conf_init(unsigned long ram_size)
{
+ static const uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
FWCfgState *fw_cfg;
hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base;
@@ -274,6 +289,10 @@ static void fw_conf_init(unsigned long ram_size)
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1);
fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz());
+
+ fw_cfg_add_file(fw_cfg, "etc/system-states",
+ g_memdup2(suspend, sizeof(suspend)), sizeof(suspend));
+
qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
}
@@ -553,6 +572,7 @@ static void mips_loongson3_virt_init(MachineState *machine)
machine->ram, 0, virt_memmap[VIRT_LOWMEM].size);
memory_region_init_io(iomem, NULL, &loongson3_pm_ops,
NULL, "loongson3_pm", virt_memmap[VIRT_PM].size);
+ qemu_register_wakeup_support();
memory_region_add_subregion(address_space_mem,
virt_memmap[VIRT_LOWMEM].base, ram);
diff --git a/hw/mips/meson.build b/hw/mips/meson.build
index f06d88f343..ca37c42d90 100644
--- a/hw/mips/meson.build
+++ b/hw/mips/meson.build
@@ -9,7 +9,7 @@ if 'CONFIG_TCG' in config_all_accel
mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c'))
mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
-mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt])
+mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c'))
endif
hw_arch += {'mips': mips_ss}
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index 2a976ca2b1..fa052c44db 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -23,6 +23,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "qemu/units.h"
#include "hw/pci/pci.h"
#include "hw/hw.h"
@@ -103,25 +104,25 @@ static void edu_lower_irq(EduState *edu, uint32_t val)
}
}
-static bool within(uint64_t addr, uint64_t start, uint64_t end)
+static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size,
+ uint64_t dma_start, uint64_t dma_size)
{
- return start <= addr && addr < end;
-}
-
-static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start,
- uint64_t size2)
-{
- uint64_t end1 = addr + size1;
- uint64_t end2 = start + size2;
-
- if (within(addr, start, end2) &&
- end1 > addr && end1 <= end2) {
+ uint64_t xfer_end = xfer_start + xfer_size;
+ uint64_t dma_end = dma_start + dma_size;
+
+ /*
+ * 1. ensure we aren't overflowing
+ * 2. ensure that xfer is within dma address range
+ */
+ if (dma_end >= dma_start && xfer_end >= xfer_start &&
+ xfer_start >= dma_start && xfer_end <= dma_end) {
return;
}
- hw_error("EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64
- " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!",
- addr, end1 - 1, start, end2 - 1);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64
+ " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!",
+ xfer_start, xfer_end - 1, dma_start, dma_end - 1);
}
static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr)
@@ -129,7 +130,9 @@ static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr)
dma_addr_t res = addr & edu->dma_mask;
if (addr != res) {
- printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "EDU: clamping DMA 0x%016"PRIx64" to 0x%016"PRIx64"!",
+ addr, res);
}
return res;
diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig
index 97af258b55..76b953c62c 100644
--- a/hw/openrisc/Kconfig
+++ b/hw/openrisc/Kconfig
@@ -1,5 +1,8 @@
config OR1K_SIM
bool
+ default y
+ depends on OPENRISC
+ select DEVICE_TREE
select SERIAL
select OPENCORES_ETH
select OMPIC
@@ -7,9 +10,12 @@ config OR1K_SIM
config OR1K_VIRT
bool
+ default y
+ depends on OPENRISC
imply PCI_DEVICES
imply VIRTIO_VGA
imply TEST_DEVICES
+ select DEVICE_TREE
select PCI
select PCI_EXPRESS_GENERIC_BRIDGE
select GOLDFISH_RTC
diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build
index 2dbc6365bb..82f1f0ef1c 100644
--- a/hw/openrisc/meson.build
+++ b/hw/openrisc/meson.build
@@ -1,7 +1,7 @@
openrisc_ss = ss.source_set()
openrisc_ss.add(files('cputimer.c'))
openrisc_ss.add(files('boot.c'))
-openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: [files('openrisc_sim.c'), fdt])
-openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: [files('virt.c'), fdt])
+openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: files('openrisc_sim.c'))
+openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: files('virt.c'))
hw_arch += {'openrisc': openrisc_ss}
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 37ccf9cdca..347212f4db 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -1,5 +1,7 @@
config PSERIES
bool
+ default y
+ depends on PPC64 && FDT
imply USB_OHCI_PCI
imply PCI_DEVICES
imply TEST_DEVICES
@@ -23,6 +25,8 @@ config SPAPR_RNG
config POWERNV
bool
+ default y
+ depends on PPC64 && FDT
imply PCI_DEVICES
imply TEST_DEVICES
select ISA_IPMI_BT
@@ -38,6 +42,8 @@ config POWERNV
config PPC405
bool
+ default y
+ depends on PPC
select M48T59
select PFLASH_CFI02
select PPC4XX
@@ -45,6 +51,8 @@ config PPC405
config PPC440
bool
+ default y
+ depends on PPC && FDT
imply PCI_DEVICES
imply TEST_DEVICES
imply E1000_PCI
@@ -62,6 +70,8 @@ config PPC4XX
config SAM460EX
bool
+ default y
+ depends on PPC && FDT
select PFLASH_CFI01
select IDE_SII3112
select M41T80
@@ -75,6 +85,8 @@ config SAM460EX
config AMIGAONE
bool
+ default y
+ depends on PPC
imply ATI_VGA
select ARTICIA
select VT82C686
@@ -82,6 +94,8 @@ config AMIGAONE
config PEGASOS2
bool
+ default y
+ depends on PPC
imply ATI_VGA
select MV64361
select VT82C686
@@ -90,6 +104,8 @@ config PEGASOS2
config PREP
bool
+ default y
+ depends on PPC
imply PCI_DEVICES
imply TEST_DEVICES
select CS4231A
@@ -106,6 +122,8 @@ config RS6000_MC
config MAC_OLDWORLD
bool
+ default y
+ depends on PPC
imply PCI_DEVICES
imply SUNGEM
imply TEST_DEVICES
@@ -117,6 +135,8 @@ config MAC_OLDWORLD
config MAC_NEWWORLD
bool
+ default y
+ depends on PPC
imply PCI_DEVICES
imply SUNGEM
imply TEST_DEVICES
@@ -147,14 +167,20 @@ config E500
config E500PLAT
bool
+ default y
+ depends on PPC && FDT
select E500
config MPC8544DS
bool
+ default y
+ depends on PPC && FDT
select E500
config VIRTEX
bool
+ default y
+ depends on PPC && FDT
select PPC4XX
select PFLASH_CFI01
select SERIAL
@@ -167,6 +193,7 @@ config FW_CFG_PPC
bool
config FDT_PPC
+ select DEVICE_TREE
bool
config VOF
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index d096636ee7..3ebbf329bc 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -3,9 +3,7 @@ ppc_ss.add(files(
'ppc.c',
'ppc_booke.c',
))
-ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: [files(
- 'fdt.c',
-), fdt])
+ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: files('fdt.c'))
ppc_ss.add(when: 'CONFIG_FW_CFG_PPC', if_true: files('fw_cfg.c'))
# IBM pSeries (sPAPR)
diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c
index 4092ebc1ab..c44e7ed162 100644
--- a/hw/ppc/ppc405_boards.c
+++ b/hw/ppc/ppc405_boards.c
@@ -350,6 +350,7 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data)
mc->init = ppc405_init;
mc->default_ram_size = 128 * MiB;
mc->default_ram_id = "ppc405.ram";
+ mc->deprecation_reason = "machine is old and unmaintained";
}
static const TypeInfo ppc405_machine_type = {
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index e18f57efce..73f80cf706 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -15,6 +15,7 @@
#include "qemu/units.h"
#include "qemu/datadir.h"
#include "qemu/error-report.h"
+#include "exec/page-protection.h"
#include "net/net.h"
#include "hw/pci/pci.h"
#include "hw/boards.h"
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
index d42b677898..8dc75fb9f0 100644
--- a/hw/ppc/sam460ex.c
+++ b/hw/ppc/sam460ex.c
@@ -21,6 +21,7 @@
#include "kvm_ppc.h"
#include "sysemu/device_tree.h"
#include "sysemu/block-backend.h"
+#include "exec/page-protection.h"
#include "hw/loader.h"
#include "elf.h"
#include "exec/memory.h"
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 72cfba419a..7cf9904c35 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -2188,10 +2188,9 @@ static int spapr_pci_post_load(void *opaque, int version_id)
int i;
for (i = 0; i < sphb->msi_devs_num; ++i) {
- key = g_memdup(&sphb->msi_devs[i].key,
- sizeof(sphb->msi_devs[i].key));
- value = g_memdup(&sphb->msi_devs[i].value,
- sizeof(sphb->msi_devs[i].value));
+ key = g_memdup2(&sphb->msi_devs[i].key, sizeof(sphb->msi_devs[i].key));
+ value = g_memdup2(&sphb->msi_devs[i].value,
+ sizeof(sphb->msi_devs[i].value));
g_hash_table_insert(sphb->msi, key, value);
}
g_free(sphb->msi_devs);
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index d02f330650..c49da1f46f 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -25,6 +25,7 @@
#include "qemu/osdep.h"
#include "qemu/datadir.h"
#include "qemu/units.h"
+#include "exec/page-protection.h"
#include "cpu.h"
#include "hw/sysbus.h"
#include "hw/char/serial.h"
diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c
index d9b879e056..8dbafafb9e 100644
--- a/hw/remote/vfio-user-obj.c
+++ b/hw/remote/vfio-user-obj.c
@@ -281,7 +281,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf,
while (bytes > 0) {
len = (bytes > pci_access_width) ? pci_access_width : bytes;
if (is_write) {
- memcpy(&val, ptr, len);
+ val = ldn_le_p(ptr, len);
pci_host_config_write_common(o->pci_dev, offset,
pci_config_size(o->pci_dev),
val, len);
@@ -289,7 +289,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf,
} else {
val = pci_host_config_read_common(o->pci_dev, offset,
pci_config_size(o->pci_dev), len);
- memcpy(ptr, &val, len);
+ stn_le_p(ptr, len, val);
trace_vfu_cfg_read(offset, val);
}
offset += len;
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index fc72ef0379..a2030e3a6f 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -8,8 +8,11 @@ config IBEX
config MICROCHIP_PFSOC
bool
+ default y
+ depends on RISCV64
select CADENCE_SDHCI
select CPU_CLUSTER
+ select DEVICE_TREE
select MCHP_PFSOC_DMC
select MCHP_PFSOC_IOSCB
select MCHP_PFSOC_MMUART
@@ -21,16 +24,21 @@ config MICROCHIP_PFSOC
config OPENTITAN
bool
+ default y
+ depends on RISCV32
select IBEX
select SIFIVE_PLIC
select UNIMP
config RISCV_VIRT
bool
+ default y
+ depends on RISCV32 || RISCV64
imply PCI_DEVICES
imply VIRTIO_VGA
imply TEST_DEVICES
imply TPM_TIS_SYSBUS
+ select DEVICE_TREE
select RISCV_NUMA
select GOLDFISH_RTC
select PCI
@@ -51,6 +59,8 @@ config RISCV_VIRT
config SHAKTI_C
bool
+ default y
+ depends on RISCV64
select RISCV_ACLINT
select SHAKTI_UART
select SIFIVE_PLIC
@@ -58,6 +68,8 @@ config SHAKTI_C
config SIFIVE_E
bool
+ default y
+ depends on RISCV32 || RISCV64
select RISCV_ACLINT
select SIFIVE_GPIO
select SIFIVE_PLIC
@@ -68,8 +80,11 @@ config SIFIVE_E
config SIFIVE_U
bool
+ default y
+ depends on RISCV32 || RISCV64
select CADENCE
select CPU_CLUSTER
+ select DEVICE_TREE
select RISCV_ACLINT
select SIFIVE_GPIO
select SIFIVE_PDMA
@@ -85,6 +100,9 @@ config SIFIVE_U
config SPIKE
bool
+ default y
+ depends on RISCV32 || RISCV64
+ select DEVICE_TREE
select RISCV_NUMA
select HTIF
select RISCV_ACLINT
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 2f7ee81be3..f872674093 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -1,5 +1,5 @@
riscv_ss = ss.source_set()
-riscv_ss.add(files('boot.c'), fdt)
+riscv_ss.add(files('boot.c'))
riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
riscv_ss.add(files('riscv_hart.c'))
riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
index ac28c1165b..052201c2cd 100644
--- a/hw/rtc/ls7a_rtc.c
+++ b/hw/rtc/ls7a_rtc.c
@@ -8,7 +8,7 @@
#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
-#include "include/hw/register.h"
+#include "hw/register.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "qemu/cutils.h"
diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c
index 3379f92748..8ccee9a385 100644
--- a/hw/rtc/mc146818rtc.c
+++ b/hw/rtc/mc146818rtc.c
@@ -104,16 +104,9 @@ static void rtc_coalesced_timer_update(MC146818RtcState *s)
}
}
-static QLIST_HEAD(, MC146818RtcState) rtc_devices =
- QLIST_HEAD_INITIALIZER(rtc_devices);
-
-void qmp_rtc_reset_reinjection(Error **errp)
+void rtc_reset_reinjection(MC146818RtcState *rtc)
{
- MC146818RtcState *s;
-
- QLIST_FOREACH(s, &rtc_devices, link) {
- s->irq_coalesced = 0;
- }
+ rtc->irq_coalesced = 0;
}
static bool rtc_policy_slew_deliver_irq(MC146818RtcState *s)
@@ -941,7 +934,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
object_property_add_tm(OBJECT(s), "date", rtc_get_date);
qdev_init_gpio_out(dev, &s->irq, 1);
- QLIST_INSERT_HEAD(&rtc_devices, s, link);
}
MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
index 2b297c5a6a..aa9242d1ef 100644
--- a/hw/rx/Kconfig
+++ b/hw/rx/Kconfig
@@ -7,4 +7,7 @@ config RX62N_MCU
config RX_GDBSIM
bool
+ default y
+ depends on RX && FDT
+ select DEVICE_TREE
select RX62N_MCU
diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig
index 26ad104485..3bbf4ae56e 100644
--- a/hw/s390x/Kconfig
+++ b/hw/s390x/Kconfig
@@ -1,5 +1,7 @@
config S390_CCW_VIRTIO
bool
+ default y
+ depends on S390X
imply VIRTIO_PCI
imply TERMINAL3270
imply VFIO_AP
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 295530963a..b2d5327dbf 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -23,6 +23,8 @@
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/s390-ccw.h"
+bool css_migration_enabled = true;
+
typedef struct CrwContainer {
CRW crw;
QTAILQ_ENTRY(CrwContainer) sibling;
@@ -180,7 +182,7 @@ static const VMStateDescription vmstate_orb = {
static bool vmstate_schdev_orb_needed(void *opaque)
{
- return css_migration_enabled();
+ return css_migration_enabled;
}
static const VMStateDescription vmstate_schdev_orb = {
@@ -388,7 +390,7 @@ static int subch_dev_post_load(void *opaque, int version_id)
css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s);
}
- if (css_migration_enabled()) {
+ if (css_migration_enabled) {
/* No compat voodoo to do ;) */
return 0;
}
@@ -412,7 +414,9 @@ static int subch_dev_post_load(void *opaque, int version_id)
void css_register_vmstate(void)
{
- vmstate_register(NULL, 0, &vmstate_css, &channel_subsys);
+ if (css_migration_enabled) {
+ vmstate_register(NULL, 0, &vmstate_css, &channel_subsys);
+ }
}
IndAddr *get_indicator(hwaddr ind_addr, int len)
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index f9829de953..06c1da0ece 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -523,16 +523,7 @@ static void register_types(void)
type_init(register_types)
-BusState *sclp_get_event_facility_bus(void)
+BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef)
{
- Object *busobj;
- SCLPEventsBus *sbus;
-
- busobj = object_resolve_path_type("", TYPE_SCLP_EVENTS_BUS, NULL);
- sbus = OBJECT_CHECK(SCLPEventsBus, busobj, TYPE_SCLP_EVENTS_BUS);
- if (!sbus) {
- return NULL;
- }
-
- return &sbus->qbus;
+ return BUS(&ef->sbus);
}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 4dcc213820..3d0bc3e7f2 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -50,22 +50,6 @@
static Error *pv_mig_blocker;
-S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
-{
- static MachineState *ms;
-
- if (!ms) {
- ms = MACHINE(qdev_get_machine());
- g_assert(ms->possible_cpus);
- }
-
- /* CPU address corresponds to the core_id and the index */
- if (cpu_addr >= ms->possible_cpus->len) {
- return NULL;
- }
- return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu);
-}
-
static S390CPU *s390x_new_cpu(const char *typename, uint32_t core_id,
Error **errp)
{
@@ -237,23 +221,31 @@ static void s390_create_virtio_net(BusState *bus, const char *name)
}
}
-static void s390_create_sclpconsole(const char *type, Chardev *chardev)
+static void s390_create_sclpconsole(SCLPDevice *sclp,
+ const char *type, Chardev *chardev)
{
+ SCLPEventFacility *ef = sclp->event_facility;
+ BusState *ev_fac_bus = sclp_get_event_facility_bus(ef);
DeviceState *dev;
dev = qdev_new(type);
+ object_property_add_child(OBJECT(ef), type, OBJECT(dev));
qdev_prop_set_chr(dev, "chardev", chardev);
- qdev_realize_and_unref(dev, sclp_get_event_facility_bus(), &error_fatal);
+ qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal);
}
static void ccw_init(MachineState *machine)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
+ S390CcwMachineState *ms = S390_CCW_MACHINE(machine);
int ret;
VirtualCssBus *css_bus;
DeviceState *dev;
- s390_sclp_init();
+ ms->sclp = SCLP(object_new(TYPE_SCLP));
+ object_property_add_child(OBJECT(machine), TYPE_SCLP, OBJECT(ms->sclp));
+ qdev_realize_and_unref(DEVICE(ms->sclp), NULL, &error_fatal);
+
/* init memory + setup max page size. Required for the CPU model */
s390_memory_init(machine->ram);
@@ -291,21 +283,19 @@ static void ccw_init(MachineState *machine)
s390_enable_css_support(s390_cpu_addr2state(0));
ret = css_create_css_image(VIRTUAL_CSSID, true);
-
assert(ret == 0);
- if (css_migration_enabled()) {
- css_register_vmstate();
- }
+
+ css_register_vmstate();
/* Create VirtIO network adapters */
s390_create_virtio_net(BUS(css_bus), mc->default_nic);
/* init consoles */
if (serial_hd(0)) {
- s390_create_sclpconsole("sclpconsole", serial_hd(0));
+ s390_create_sclpconsole(ms->sclp, "sclpconsole", serial_hd(0));
}
if (serial_hd(1)) {
- s390_create_sclpconsole("sclplmconsole", serial_hd(1));
+ s390_create_sclpconsole(ms->sclp, "sclplmconsole", serial_hd(1));
}
/* init the TOD clock */
@@ -757,7 +747,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
s390mc->ri_allowed = true;
s390mc->cpu_model_allowed = true;
- s390mc->css_migration_enabled = true;
s390mc->hpage_1m_allowed = true;
s390mc->max_threads = 1;
mc->init = ccw_init;
@@ -827,11 +816,6 @@ static const TypeInfo ccw_machine_info = {
},
};
-bool css_migration_enabled(void)
-{
- return get_machine_class()->css_migration_enabled;
-}
-
#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \
static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \
void *data) \
@@ -1187,15 +1171,15 @@ static void ccw_machine_2_9_instance_options(MachineState *machine)
static void ccw_machine_2_9_class_options(MachineClass *mc)
{
- S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc);
static GlobalProperty compat[] = {
{ TYPE_S390_STATTRIB, "migration-enabled", "off", },
+ { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", },
};
ccw_machine_2_10_class_options(mc);
compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len);
compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
- s390mc->css_migration_enabled = false;
+ css_migration_enabled = false;
}
DEFINE_CCW_MACHINE(2_9, "2.9", false);
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index 893e71a41b..e725dcd5fd 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -21,13 +21,14 @@
#include "hw/s390x/s390-pci-bus.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/cpu-topology.h"
+#include "hw/s390x/s390-virtio-ccw.h"
-static inline SCLPDevice *get_sclp_device(void)
+static SCLPDevice *get_sclp_device(void)
{
static SCLPDevice *sclp;
if (!sclp) {
- sclp = SCLP(object_resolve_path_type("", TYPE_SCLP, NULL));
+ sclp = S390_CCW_MACHINE(qdev_get_machine())->sclp;
}
return sclp;
}
@@ -378,16 +379,6 @@ void sclp_service_interrupt(uint32_t sccb)
}
/* qemu object creation and initialization functions */
-
-void s390_sclp_init(void)
-{
- Object *new = object_new(TYPE_SCLP);
-
- object_property_add_child(qdev_get_machine(), TYPE_SCLP, new);
- object_unref(new);
- qdev_realize(DEVICE(new), NULL, &error_fatal);
-}
-
static void sclp_realize(DeviceState *dev, Error **errp)
{
MachineState *machine = MACHINE(qdev_get_machine());
diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig
index e0c4ecd1a5..99a76a94c3 100644
--- a/hw/sh4/Kconfig
+++ b/hw/sh4/Kconfig
@@ -1,5 +1,7 @@
config R2D
bool
+ default y
+ depends on SH4
imply PCI_DEVICES
imply TEST_DEVICES
imply RTL8139_PCI
@@ -13,6 +15,8 @@ config R2D
config SHIX
bool
+ default y
+ depends on SH4
select SH7750
select TC58128
diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build
index 424d5674de..70e814c3a2 100644
--- a/hw/sh4/meson.build
+++ b/hw/sh4/meson.build
@@ -1,5 +1,5 @@
sh4_ss = ss.source_set()
-sh4_ss.add(files(
+sh4_ss.add(when: 'CONFIG_SH7750', if_true: files(
'sh7750.c',
'sh7750_regnames.c',
))
diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig
index 79d58beb7a..3cc165dbfb 100644
--- a/hw/sparc/Kconfig
+++ b/hw/sparc/Kconfig
@@ -1,5 +1,7 @@
config SUN4M
bool
+ default y
+ depends on SPARC && !SPARC64
imply TCX
imply CG3
select CS4231
@@ -18,6 +20,8 @@ config SUN4M
config LEON3
bool
+ default y
+ depends on SPARC && !SPARC64
select GRLIB
config GRLIB
diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig
index 7e557ad17b..3b948a2290 100644
--- a/hw/sparc64/Kconfig
+++ b/hw/sparc64/Kconfig
@@ -1,5 +1,7 @@
config SUN4U
bool
+ default y
+ depends on SPARC64
imply PCI_DEVICES
imply SUNHME
imply TEST_DEVICES
@@ -16,6 +18,8 @@ config SUN4U
config NIAGARA
bool
+ default y
+ depends on SPARC64
select EMPTY_SLOT
select SUN4V_RTC
select UNIMP
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index cff6d5abaf..4ece1ac1ff 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -793,6 +793,12 @@ static void sun4v_init(MachineState *machine)
sun4uv_init(get_system_memory(), machine, &hwdefs[1]);
}
+static GlobalProperty hw_compat_sparc64[] = {
+ { "virtio-pci", "disable-legacy", "on", .optional = true },
+ { "virtio-device", "iommu_platform", "on" },
+};
+static const size_t hw_compat_sparc64_len = G_N_ELEMENTS(hw_compat_sparc64);
+
static void sun4u_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -810,6 +816,7 @@ static void sun4u_class_init(ObjectClass *oc, void *data)
mc->default_nic = "sunhme";
mc->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL);
fwc->get_dev_path = sun4u_fw_dev_path;
+ compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len);
}
static const TypeInfo sun4u_type = {
diff --git a/hw/tricore/Kconfig b/hw/tricore/Kconfig
index 33c1e852c3..6c04f64949 100644
--- a/hw/tricore/Kconfig
+++ b/hw/tricore/Kconfig
@@ -1,8 +1,12 @@
config TRICORE_TESTBOARD
+ default y
+ depends on TRICORE
bool
config TRIBOARD
bool
+ default y
+ depends on TRICORE
select TC27X_SOC
config TC27X_SOC
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
index eccdb852a0..bac78a32bb 100644
--- a/hw/ufs/ufs.c
+++ b/hw/ufs/ufs.c
@@ -126,6 +126,10 @@ static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req)
copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
data_segment_length;
+ if (copy_size > sizeof(req->req_upiu)) {
+ copy_size = sizeof(req->req_upiu);
+ }
+
ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size);
if (ret) {
trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
@@ -225,6 +229,10 @@ static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req)
copy_size = rsp_upiu_byte_len;
}
+ if (copy_size > sizeof(req->rsp_upiu)) {
+ copy_size = sizeof(req->rsp_upiu);
+ }
+
ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size);
if (ret) {
trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr);
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 2c33e36cad..d00d68b21d 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -475,14 +475,6 @@ struct rndis_packet_msg_type {
le32 Reserved;
};
-struct rndis_config_parameter {
- le32 ParameterNameOffset;
- le32 ParameterNameLength;
- le32 ParameterType;
- le32 ParameterValueOffset;
- le32 ParameterValueLength;
-};
-
/* implementation specific */
enum rndis_state
{
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
index 09ec326aea..416623f956 100644
--- a/hw/usb/xen-usb.c
+++ b/hw/usb/xen-usb.c
@@ -1083,7 +1083,7 @@ static void usbback_event(struct XenLegacyDevice *xendev)
qemu_bh_schedule(usbif->bh);
}
-struct XenDevOps xen_usb_ops = {
+static struct XenDevOps xen_usb_ops = {
.size = sizeof(struct usbback_info),
.flags = DEVOPS_FLAG_NEED_GNTDEV,
.init = usbback_init,
@@ -1095,15 +1095,9 @@ struct XenDevOps xen_usb_ops = {
.event = usbback_event,
};
-#else /* USBIF_SHORT_NOT_OK */
-
-static int usbback_not_supported(void)
+static void xen_usb_register_backend(void)
{
- return -EINVAL;
+ xen_be_register("qusb", &xen_usb_ops);
}
-
-struct XenDevOps xen_usb_ops = {
- .backend_register = usbback_not_supported,
-};
-
+xen_backend_init(xen_usb_register_backend);
#endif
diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
index 7c4caa5938..c12531a788 100644
--- a/hw/vfio/ap.c
+++ b/hw/vfio/ap.c
@@ -70,14 +70,14 @@ static void vfio_ap_req_notifier_handler(void *opaque)
}
}
-static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
+static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
unsigned int irq, Error **errp)
{
int fd;
size_t argsz;
IOHandler *fd_read;
EventNotifier *notifier;
- struct vfio_irq_info *irq_info;
+ g_autofree struct vfio_irq_info *irq_info = NULL;
VFIODevice *vdev = &vapdev->vdev;
switch (irq) {
@@ -87,13 +87,13 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
break;
default:
error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
- return;
+ return false;
}
if (vdev->num_irqs < irq + 1) {
error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)",
irq, vdev->num_irqs);
- return;
+ return false;
}
argsz = sizeof(*irq_info);
@@ -104,28 +104,26 @@ static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
irq_info) < 0 || irq_info->count < 1) {
error_setg_errno(errp, errno, "vfio: Error getting irq info");
- goto out_free_info;
+ return false;
}
if (event_notifier_init(notifier, 0)) {
error_setg_errno(errp, errno,
"vfio: Unable to init event notifier for irq (%d)",
irq);
- goto out_free_info;
+ return false;
}
fd = event_notifier_get_fd(notifier);
qemu_set_fd_handler(fd, fd_read, NULL, vapdev);
- if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
- errp)) {
+ if (!vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
+ errp)) {
qemu_set_fd_handler(fd, NULL, NULL, vapdev);
event_notifier_cleanup(notifier);
}
-out_free_info:
- g_free(irq_info);
-
+ return true;
}
static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
@@ -143,8 +141,8 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
return;
}
- if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
+ if (!vfio_set_irq_signaling(&vapdev->vdev, irq, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
}
@@ -156,23 +154,20 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
static void vfio_ap_realize(DeviceState *dev, Error **errp)
{
ERRP_GUARD();
- int ret;
Error *err = NULL;
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
VFIODevice *vbasedev = &vapdev->vdev;
- if (vfio_device_get_name(vbasedev, errp) < 0) {
+ if (!vfio_device_get_name(vbasedev, errp)) {
return;
}
- ret = vfio_attach_device(vbasedev->name, vbasedev,
- &address_space_memory, errp);
- if (ret) {
+ if (!vfio_attach_device(vbasedev->name, vbasedev,
+ &address_space_memory, errp)) {
goto error;
}
- vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err);
- if (err) {
+ if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err)) {
/*
* Report this error, but do not make it a failing condition.
* Lack of this IRQ in the host does not prevent normal operation.
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index 90e4a53437..2600e62e37 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -379,12 +379,12 @@ read_err:
css_inject_io_interrupt(sch);
}
-static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
+static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
unsigned int irq,
Error **errp)
{
VFIODevice *vdev = &vcdev->vdev;
- struct vfio_irq_info *irq_info;
+ g_autofree struct vfio_irq_info *irq_info = NULL;
size_t argsz;
int fd;
EventNotifier *notifier;
@@ -405,13 +405,13 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
break;
default:
error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
- return;
+ return false;
}
if (vdev->num_irqs < irq + 1) {
error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)",
irq, vdev->num_irqs);
- return;
+ return false;
}
argsz = sizeof(*irq_info);
@@ -421,27 +421,26 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
irq_info) < 0 || irq_info->count < 1) {
error_setg_errno(errp, errno, "vfio: Error getting irq info");
- goto out_free_info;
+ return false;
}
if (event_notifier_init(notifier, 0)) {
error_setg_errno(errp, errno,
"vfio: Unable to init event notifier for irq (%d)",
irq);
- goto out_free_info;
+ return false;
}
fd = event_notifier_get_fd(notifier);
qemu_set_fd_handler(fd, fd_read, NULL, vcdev);
- if (vfio_set_irq_signaling(vdev, irq, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
+ if (!vfio_set_irq_signaling(vdev, irq, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
qemu_set_fd_handler(fd, NULL, NULL, vcdev);
event_notifier_cleanup(notifier);
}
-out_free_info:
- g_free(irq_info);
+ return true;
}
static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev,
@@ -465,8 +464,8 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev,
return;
}
- if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
+ if (!vfio_set_irq_signaling(&vcdev->vdev, irq, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name);
}
@@ -475,7 +474,7 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev,
event_notifier_cleanup(notifier);
}
-static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
+static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
{
VFIODevice *vdev = &vcdev->vdev;
struct vfio_region_info *info;
@@ -484,7 +483,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
/* Sanity check device */
if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) {
error_setg(errp, "vfio: Um, this isn't a vfio-ccw device");
- return;
+ return false;
}
/*
@@ -494,13 +493,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
error_setg(errp, "vfio: too few regions (%u), expected at least %u",
vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1);
- return;
+ return false;
}
ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info);
if (ret) {
error_setg_errno(errp, -ret, "vfio: Error getting config info");
- return;
+ return false;
}
vcdev->io_region_size = info->size;
@@ -554,7 +553,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
g_free(info);
}
- return;
+ return true;
out_err:
g_free(vcdev->crw_region);
@@ -562,7 +561,7 @@ out_err:
g_free(vcdev->async_cmd_region);
g_free(vcdev->io_region);
g_free(info);
- return;
+ return false;
}
static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
@@ -580,7 +579,6 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp)
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
VFIODevice *vbasedev = &vcdev->vdev;
Error *err = NULL;
- int ret;
/* Call the class init function for subchannel. */
if (cdc->realize) {
@@ -590,35 +588,31 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp)
}
}
- if (vfio_device_get_name(vbasedev, errp) < 0) {
+ if (!vfio_device_get_name(vbasedev, errp)) {
return;
}
- ret = vfio_attach_device(cdev->mdevid, vbasedev,
- &address_space_memory, errp);
- if (ret) {
+ if (!vfio_attach_device(cdev->mdevid, vbasedev,
+ &address_space_memory, errp)) {
goto out_attach_dev_err;
}
- vfio_ccw_get_region(vcdev, &err);
- if (err) {
+ if (!vfio_ccw_get_region(vcdev, &err)) {
goto out_region_err;
}
- vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err);
- if (err) {
+ if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err)) {
goto out_io_notifier_err;
}
if (vcdev->crw_region) {
- vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err);
- if (err) {
+ if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX,
+ &err)) {
goto out_irq_notifier_err;
}
}
- vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err);
- if (err) {
+ if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err)) {
/*
* Report this error, but do not make it a failing condition.
* Lack of this IRQ in the host does not prevent normal operation.
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 8f9cbdc026..f9619a1dfb 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -147,10 +147,10 @@ bool vfio_viommu_preset(VFIODevice *vbasedev)
return vbasedev->bcontainer->space->as != &address_space_memory;
}
-static void vfio_set_migration_error(int err)
+static void vfio_set_migration_error(int ret)
{
if (migration_is_setup_or_active()) {
- migration_file_set_error(err);
+ migration_file_set_error(ret, NULL);
}
}
@@ -253,12 +253,13 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
/* Called with rcu_read_lock held. */
static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
- ram_addr_t *ram_addr, bool *read_only)
+ ram_addr_t *ram_addr, bool *read_only,
+ Error **errp)
{
bool ret, mr_has_discard_manager;
ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only,
- &mr_has_discard_manager);
+ &mr_has_discard_manager, errp);
if (ret && mr_has_discard_manager) {
/*
* Malicious VMs might trigger discarding of IOMMU-mapped memory. The
@@ -288,6 +289,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
hwaddr iova = iotlb->iova + giommu->iommu_offset;
void *vaddr;
int ret;
+ Error *local_err = NULL;
trace_vfio_iommu_map_notify(iotlb->perm == IOMMU_NONE ? "UNMAP" : "MAP",
iova, iova + iotlb->addr_mask);
@@ -304,7 +306,8 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
bool read_only;
- if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only)) {
+ if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &local_err)) {
+ error_report_err(local_err);
goto out;
}
/*
@@ -585,7 +588,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
return;
}
- if (vfio_container_add_section_window(bcontainer, section, &err)) {
+ if (!vfio_container_add_section_window(bcontainer, section, &err)) {
goto fail;
}
@@ -1027,7 +1030,8 @@ static void vfio_device_feature_dma_logging_start_destroy(
g_free(feature);
}
-static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer)
+static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer,
+ Error **errp)
{
struct vfio_device_feature *feature;
VFIODirtyRanges ranges;
@@ -1038,6 +1042,7 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer)
feature = vfio_device_feature_dma_logging_start_create(bcontainer,
&ranges);
if (!feature) {
+ error_setg_errno(errp, errno, "Failed to prepare DMA logging");
return -errno;
}
@@ -1049,8 +1054,8 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer)
ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature);
if (ret) {
ret = -errno;
- error_report("%s: Failed to start DMA logging, err %d (%s)",
- vbasedev->name, ret, strerror(errno));
+ error_setg_errno(errp, errno, "%s: Failed to start DMA logging",
+ vbasedev->name);
goto out;
}
vbasedev->dirty_tracking = true;
@@ -1069,20 +1074,19 @@ out:
static bool vfio_listener_log_global_start(MemoryListener *listener,
Error **errp)
{
+ ERRP_GUARD();
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
listener);
int ret;
if (vfio_devices_all_device_dirty_tracking(bcontainer)) {
- ret = vfio_devices_dma_logging_start(bcontainer);
+ ret = vfio_devices_dma_logging_start(bcontainer, errp);
} else {
- ret = vfio_container_set_dirty_page_tracking(bcontainer, true);
+ ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp);
}
if (ret) {
- error_report("vfio: Could not start dirty page tracking, err: %d (%s)",
- ret, strerror(-ret));
- vfio_set_migration_error(ret);
+ error_prepend(errp, "vfio: Could not start dirty page tracking - ");
}
return !ret;
}
@@ -1091,17 +1095,20 @@ static void vfio_listener_log_global_stop(MemoryListener *listener)
{
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
listener);
+ Error *local_err = NULL;
int ret = 0;
if (vfio_devices_all_device_dirty_tracking(bcontainer)) {
vfio_devices_dma_logging_stop(bcontainer);
} else {
- ret = vfio_container_set_dirty_page_tracking(bcontainer, false);
+ ret = vfio_container_set_dirty_page_tracking(bcontainer, false,
+ &local_err);
}
if (ret) {
- error_report("vfio: Could not stop dirty page tracking, err: %d (%s)",
- ret, strerror(-ret));
+ error_prepend(&local_err,
+ "vfio: Could not stop dirty page tracking - ");
+ error_report_err(local_err);
vfio_set_migration_error(ret);
}
}
@@ -1133,8 +1140,7 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova,
}
int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap, hwaddr iova,
- hwaddr size)
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
{
VFIODevice *vbasedev;
int ret;
@@ -1143,10 +1149,10 @@ int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
ret = vfio_device_dma_logging_report(vbasedev, iova, size,
vbmap->bitmap);
if (ret) {
- error_report("%s: Failed to get DMA logging report, iova: "
- "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx
- ", err: %d (%s)",
- vbasedev->name, iova, size, ret, strerror(-ret));
+ error_setg_errno(errp, -ret,
+ "%s: Failed to get DMA logging report, iova: "
+ "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx,
+ vbasedev->name, iova, size);
return ret;
}
@@ -1156,7 +1162,7 @@ int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
}
int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova,
- uint64_t size, ram_addr_t ram_addr)
+ uint64_t size, ram_addr_t ram_addr, Error **errp)
{
bool all_device_dirty_tracking =
vfio_devices_all_device_dirty_tracking(bcontainer);
@@ -1173,13 +1179,17 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova,
ret = vfio_bitmap_alloc(&vbmap, size);
if (ret) {
+ error_setg_errno(errp, -ret,
+ "Failed to allocate dirty tracking bitmap");
return ret;
}
if (all_device_dirty_tracking) {
- ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size);
+ ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
+ errp);
} else {
- ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size);
+ ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size,
+ errp);
}
if (ret) {
@@ -1209,6 +1219,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
VFIOContainerBase *bcontainer = giommu->bcontainer;
hwaddr iova = iotlb->iova + giommu->iommu_offset;
ram_addr_t translated_addr;
+ Error *local_err = NULL;
int ret = -EINVAL;
trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask);
@@ -1220,16 +1231,22 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
}
rcu_read_lock();
- if (vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL)) {
- ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1,
- translated_addr);
- if (ret) {
- error_report("vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%s)",
- bcontainer, iova, iotlb->addr_mask + 1, ret,
- strerror(-ret));
- }
+ if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) {
+ error_report_err(local_err);
+ goto out_unlock;
}
+
+ ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1,
+ translated_addr, &local_err);
+ if (ret) {
+ error_prepend(&local_err,
+ "vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", "
+ "0x%"HWADDR_PRIx") failed - ", bcontainer, iova,
+ iotlb->addr_mask + 1);
+ error_report_err(local_err);
+ }
+
+out_unlock:
rcu_read_unlock();
out:
@@ -1246,12 +1263,19 @@ static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section,
const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) +
section->offset_within_region;
VFIORamDiscardListener *vrdl = opaque;
+ Error *local_err = NULL;
+ int ret;
/*
* Sync the whole mapped region (spanning multiple individual mappings)
* in one go.
*/
- return vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr);
+ ret = vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr,
+ &local_err);
+ if (ret) {
+ error_report_err(local_err);
+ }
+ return ret;
}
static int
@@ -1283,7 +1307,7 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer,
}
static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer,
- MemoryRegionSection *section)
+ MemoryRegionSection *section, Error **errp)
{
ram_addr_t ram_addr;
@@ -1314,7 +1338,14 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer,
}
return 0;
} else if (memory_region_has_ram_discard_manager(section->mr)) {
- return vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section);
+ int ret;
+
+ ret = vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section);
+ if (ret) {
+ error_setg(errp,
+ "Failed to sync dirty bitmap with RAM discard listener");
+ }
+ return ret;
}
ram_addr = memory_region_get_ram_addr(section->mr) +
@@ -1322,7 +1353,7 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer,
return vfio_get_dirty_bitmap(bcontainer,
REAL_HOST_PAGE_ALIGN(section->offset_within_address_space),
- int128_get64(section->size), ram_addr);
+ int128_get64(section->size), ram_addr, errp);
}
static void vfio_listener_log_sync(MemoryListener *listener,
@@ -1331,16 +1362,16 @@ static void vfio_listener_log_sync(MemoryListener *listener,
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
listener);
int ret;
+ Error *local_err = NULL;
if (vfio_listener_skipped_section(section)) {
return;
}
if (vfio_devices_all_dirty_tracking(bcontainer)) {
- ret = vfio_sync_dirty_bitmap(bcontainer, section);
+ ret = vfio_sync_dirty_bitmap(bcontainer, section, &local_err);
if (ret) {
- error_report("vfio: Failed to sync dirty bitmap, err: %d (%s)", ret,
- strerror(-ret));
+ error_report_err(local_err);
vfio_set_migration_error(ret);
}
}
@@ -1492,8 +1523,8 @@ retry:
return info;
}
-int vfio_attach_device(char *name, VFIODevice *vbasedev,
- AddressSpace *as, Error **errp)
+bool vfio_attach_device(char *name, VFIODevice *vbasedev,
+ AddressSpace *as, Error **errp)
{
const VFIOIOMMUClass *ops =
VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY));
diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c
index 913ae49077..760d9d0622 100644
--- a/hw/vfio/container-base.c
+++ b/hw/vfio/container-base.c
@@ -31,12 +31,12 @@ int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
return bcontainer->ops->dma_unmap(bcontainer, iova, size, iotlb);
}
-int vfio_container_add_section_window(VFIOContainerBase *bcontainer,
- MemoryRegionSection *section,
- Error **errp)
+bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
+ MemoryRegionSection *section,
+ Error **errp)
{
if (!bcontainer->ops->add_window) {
- return 0;
+ return true;
}
return bcontainer->ops->add_window(bcontainer, section, errp);
@@ -53,22 +53,22 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer,
}
int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer,
- bool start)
+ bool start, Error **errp)
{
if (!bcontainer->dirty_pages_supported) {
return 0;
}
g_assert(bcontainer->ops->set_dirty_page_tracking);
- return bcontainer->ops->set_dirty_page_tracking(bcontainer, start);
+ return bcontainer->ops->set_dirty_page_tracking(bcontainer, start, errp);
}
int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap,
- hwaddr iova, hwaddr size)
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
{
g_assert(bcontainer->ops->query_dirty_bitmap);
- return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size);
+ return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size,
+ errp);
}
void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space,
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 77bdec276e..096cc97258 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -130,6 +130,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
};
bool need_dirty_sync = false;
int ret;
+ Error *local_err = NULL;
if (iotlb && vfio_devices_all_running_and_mig_active(bcontainer)) {
if (!vfio_devices_all_device_dirty_tracking(bcontainer) &&
@@ -165,8 +166,9 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
if (need_dirty_sync) {
ret = vfio_get_dirty_bitmap(bcontainer, iova, size,
- iotlb->translated_addr);
+ iotlb->translated_addr, &local_err);
if (ret) {
+ error_report_err(local_err);
return ret;
}
}
@@ -209,7 +211,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
static int
vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer,
- bool start)
+ bool start, Error **errp)
{
const VFIOContainer *container = container_of(bcontainer, VFIOContainer,
bcontainer);
@@ -227,16 +229,15 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer,
ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty);
if (ret) {
ret = -errno;
- error_report("Failed to set dirty tracking flag 0x%x errno: %d",
- dirty.flags, errno);
+ error_setg_errno(errp, errno, "Failed to set dirty tracking flag 0x%x",
+ dirty.flags);
}
return ret;
}
static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap,
- hwaddr iova, hwaddr size)
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
{
const VFIOContainer *container = container_of(bcontainer, VFIOContainer,
bcontainer);
@@ -264,9 +265,10 @@ static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, dbitmap);
if (ret) {
ret = -errno;
- error_report("Failed to get dirty bitmap for iova: 0x%"PRIx64
- " size: 0x%"PRIx64" err: %d", (uint64_t)range->iova,
- (uint64_t)range->size, errno);
+ error_setg_errno(errp, errno,
+ "Failed to get dirty bitmap for iova: 0x%"PRIx64
+ " size: 0x%"PRIx64, (uint64_t)range->iova,
+ (uint64_t)range->size);
}
g_free(dbitmap);
@@ -391,21 +393,20 @@ static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp)
return VFIO_IOMMU_CLASS(klass);
}
-static int vfio_set_iommu(VFIOContainer *container, int group_fd,
- VFIOAddressSpace *space, Error **errp)
+static bool vfio_set_iommu(VFIOContainer *container, int group_fd,
+ VFIOAddressSpace *space, Error **errp)
{
- int iommu_type, ret;
+ int iommu_type;
const VFIOIOMMUClass *vioc;
iommu_type = vfio_get_iommu_type(container, errp);
if (iommu_type < 0) {
- return iommu_type;
+ return false;
}
- ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd);
- if (ret) {
+ if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
error_setg_errno(errp, errno, "Failed to set group container");
- return -errno;
+ return false;
}
while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) {
@@ -420,7 +421,7 @@ static int vfio_set_iommu(VFIOContainer *container, int group_fd,
continue;
}
error_setg_errno(errp, errno, "Failed to set iommu for container");
- return -errno;
+ return false;
}
container->iommu_type = iommu_type;
@@ -428,11 +429,11 @@ static int vfio_set_iommu(VFIOContainer *container, int group_fd,
vioc = vfio_get_iommu_class(iommu_type, errp);
if (!vioc) {
error_setg(errp, "No available IOMMU models");
- return -EINVAL;
+ return false;
}
vfio_container_init(&container->bcontainer, space, vioc);
- return 0;
+ return true;
}
static int vfio_get_iommu_info(VFIOContainer *container,
@@ -505,7 +506,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container,
}
}
-static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp)
+static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp)
{
VFIOContainer *container = container_of(bcontainer, VFIOContainer,
bcontainer);
@@ -515,7 +516,7 @@ static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp)
ret = vfio_get_iommu_info(container, &info);
if (ret) {
error_setg_errno(errp, -ret, "Failed to get VFIO IOMMU info");
- return ret;
+ return false;
}
if (info->flags & VFIO_IOMMU_INFO_PGSIZES) {
@@ -531,11 +532,11 @@ static int vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp)
vfio_get_info_iova_range(info, bcontainer);
vfio_get_iommu_info_migration(container, info);
- return 0;
+ return true;
}
-static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
- Error **errp)
+static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as,
+ Error **errp)
{
VFIOContainer *container;
VFIOContainerBase *bcontainer;
@@ -587,19 +588,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
error_report("vfio: error disconnecting group %d from"
" container", group->groupid);
}
- return ret;
+ return false;
}
group->container = container;
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
vfio_kvm_device_add_group(group);
- return 0;
+ return true;
}
}
fd = qemu_open_old("/dev/vfio/vfio", O_RDWR);
if (fd < 0) {
error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio");
- ret = -errno;
goto put_space_exit;
}
@@ -607,7 +607,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
if (ret != VFIO_API_VERSION) {
error_setg(errp, "supported vfio version: %d, "
"reported version: %d", VFIO_API_VERSION, ret);
- ret = -EINVAL;
goto close_fd_exit;
}
@@ -615,13 +614,11 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
container->fd = fd;
bcontainer = &container->bcontainer;
- ret = vfio_set_iommu(container, group->fd, space, errp);
- if (ret) {
+ if (!vfio_set_iommu(container, group->fd, space, errp)) {
goto free_container_exit;
}
- ret = vfio_cpr_register_container(bcontainer, errp);
- if (ret) {
+ if (!vfio_cpr_register_container(bcontainer, errp)) {
goto free_container_exit;
}
@@ -633,8 +630,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
assert(bcontainer->ops->setup);
- ret = bcontainer->ops->setup(bcontainer, errp);
- if (ret) {
+ if (!bcontainer->ops->setup(bcontainer, errp)) {
goto enable_discards_exit;
}
@@ -650,7 +646,6 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
memory_listener_register(&bcontainer->listener, bcontainer->space->as);
if (bcontainer->error) {
- ret = -1;
error_propagate_prepend(errp, bcontainer->error,
"memory listener initialization failed: ");
goto listener_release_exit;
@@ -658,7 +653,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
bcontainer->initialized = true;
- return 0;
+ return true;
listener_release_exit:
QLIST_REMOVE(group, container_next);
QLIST_REMOVE(bcontainer, next);
@@ -683,7 +678,7 @@ close_fd_exit:
put_space_exit:
vfio_put_address_space(space);
- return ret;
+ return false;
}
static void vfio_disconnect_container(VFIOGroup *group)
@@ -770,7 +765,7 @@ static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
group->groupid = groupid;
QLIST_INIT(&group->device_list);
- if (vfio_connect_container(group, as, errp)) {
+ if (!vfio_connect_container(group, as, errp)) {
error_prepend(errp, "failed to setup container for group %d: ",
groupid);
goto close_fd_exit;
@@ -806,8 +801,8 @@ static void vfio_put_group(VFIOGroup *group)
g_free(group);
}
-static int vfio_get_device(VFIOGroup *group, const char *name,
- VFIODevice *vbasedev, Error **errp)
+static bool vfio_get_device(VFIOGroup *group, const char *name,
+ VFIODevice *vbasedev, Error **errp)
{
g_autofree struct vfio_device_info *info = NULL;
int fd;
@@ -819,14 +814,14 @@ static int vfio_get_device(VFIOGroup *group, const char *name,
error_append_hint(errp,
"Verify all devices in group %d are bound to vfio-<bus> "
"or pci-stub and not already in use\n", group->groupid);
- return fd;
+ return false;
}
info = vfio_get_device_info(fd);
if (!info) {
error_setg_errno(errp, errno, "error getting device info");
close(fd);
- return -1;
+ return false;
}
/*
@@ -841,7 +836,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name,
error_setg(errp, "Inconsistent setting of support for discarding "
"RAM (e.g., balloon) within group");
close(fd);
- return -1;
+ return false;
}
if (!group->ram_block_discard_allowed) {
@@ -862,7 +857,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name,
vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET);
- return 0;
+ return true;
}
static void vfio_put_base_device(VFIODevice *vbasedev)
@@ -908,37 +903,35 @@ static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp)
* @name and @vbasedev->name are likely to be different depending
* on the type of the device, hence the need for passing @name
*/
-static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
- AddressSpace *as, Error **errp)
+static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
+ AddressSpace *as, Error **errp)
{
int groupid = vfio_device_groupid(vbasedev, errp);
VFIODevice *vbasedev_iter;
VFIOGroup *group;
VFIOContainerBase *bcontainer;
- int ret;
if (groupid < 0) {
- return groupid;
+ return false;
}
trace_vfio_attach_device(vbasedev->name, groupid);
group = vfio_get_group(groupid, as, errp);
if (!group) {
- return -ENOENT;
+ return false;
}
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
error_setg(errp, "device is already attached");
vfio_put_group(group);
- return -EBUSY;
+ return false;
}
}
- ret = vfio_get_device(group, name, vbasedev, errp);
- if (ret) {
+ if (!vfio_get_device(group, name, vbasedev, errp)) {
vfio_put_group(group);
- return ret;
+ return false;
}
bcontainer = &group->container->bcontainer;
@@ -946,7 +939,7 @@ static int vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next);
QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next);
- return ret;
+ return true;
}
static void vfio_legacy_detach_device(VFIODevice *vbasedev)
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index 392c2dd95d..87e51fcee1 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -25,12 +25,12 @@ static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier,
return 0;
}
-int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp)
+bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp)
{
migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
vfio_cpr_reboot_notifier,
MIG_MODE_CPR_REBOOT);
- return 0;
+ return true;
}
void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer)
diff --git a/hw/vfio/display.c b/hw/vfio/display.c
index 1aa440c663..661e921616 100644
--- a/hw/vfio/display.c
+++ b/hw/vfio/display.c
@@ -241,14 +241,11 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
dmabuf = g_new0(VFIODMABuf, 1);
dmabuf->dmabuf_id = plane.dmabuf_id;
- dmabuf->buf.width = plane.width;
- dmabuf->buf.height = plane.height;
- dmabuf->buf.backing_width = plane.width;
- dmabuf->buf.backing_height = plane.height;
- dmabuf->buf.stride = plane.stride;
- dmabuf->buf.fourcc = plane.drm_format;
- dmabuf->buf.modifier = plane.drm_format_mod;
- dmabuf->buf.fd = fd;
+ dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height,
+ plane.stride, 0, 0, plane.width,
+ plane.height, plane.drm_format,
+ plane.drm_format_mod, fd, false, false);
+
if (plane_type == DRM_PLANE_TYPE_CURSOR) {
vfio_display_update_cursor(dmabuf, &plane);
}
@@ -260,8 +257,10 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
{
QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
- dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf);
- close(dmabuf->buf.fd);
+
+ qemu_dmabuf_close(dmabuf->buf);
+ dpy_gl_release_dmabuf(dpy->con, dmabuf->buf);
+ g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
g_free(dmabuf);
}
@@ -286,6 +285,7 @@ static void vfio_display_dmabuf_update(void *opaque)
VFIOPCIDevice *vdev = opaque;
VFIODisplay *dpy = vdev->dpy;
VFIODMABuf *primary, *cursor;
+ uint32_t width, height;
bool free_bufs = false, new_cursor = false;
primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
@@ -296,11 +296,13 @@ static void vfio_display_dmabuf_update(void *opaque)
return;
}
+ width = qemu_dmabuf_get_width(primary->buf);
+ height = qemu_dmabuf_get_height(primary->buf);
+
if (dpy->dmabuf.primary != primary) {
dpy->dmabuf.primary = primary;
- qemu_console_resize(dpy->con,
- primary->buf.width, primary->buf.height);
- dpy_gl_scanout_dmabuf(dpy->con, &primary->buf);
+ qemu_console_resize(dpy->con, width, height);
+ dpy_gl_scanout_dmabuf(dpy->con, primary->buf);
free_bufs = true;
}
@@ -314,7 +316,7 @@ static void vfio_display_dmabuf_update(void *opaque)
if (cursor && (new_cursor || cursor->hot_updates)) {
bool have_hot = (cursor->hot_x != 0xffffffff &&
cursor->hot_y != 0xffffffff);
- dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot,
+ dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot,
cursor->hot_x, cursor->hot_y);
cursor->hot_updates = 0;
} else if (!cursor && new_cursor) {
@@ -328,7 +330,7 @@ static void vfio_display_dmabuf_update(void *opaque)
cursor->pos_updates = 0;
}
- dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height);
+ dpy_gl_update(dpy->con, 0, 0, width, height);
if (free_bufs) {
vfio_display_free_dmabufs(vdev);
@@ -346,11 +348,11 @@ static const GraphicHwOps vfio_display_dmabuf_ops = {
.ui_info = vfio_display_edid_ui_info,
};
-static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
{
if (!display_opengl) {
error_setg(errp, "vfio-display-dmabuf: opengl not available");
- return -1;
+ return false;
}
vdev->dpy = g_new0(VFIODisplay, 1);
@@ -359,9 +361,12 @@ static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
vdev);
if (vdev->enable_ramfb) {
vdev->dpy->ramfb = ramfb_setup(errp);
+ if (!vdev->dpy->ramfb) {
+ return false;
+ }
}
vfio_display_edid_init(vdev);
- return 0;
+ return true;
}
static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
@@ -478,7 +483,7 @@ static const GraphicHwOps vfio_display_region_ops = {
.gfx_update = vfio_display_region_update,
};
-static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
{
vdev->dpy = g_new0(VFIODisplay, 1);
vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
@@ -486,8 +491,11 @@ static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
vdev);
if (vdev->enable_ramfb) {
vdev->dpy->ramfb = ramfb_setup(errp);
+ if (!vdev->dpy->ramfb) {
+ return false;
+ }
}
- return 0;
+ return true;
}
static void vfio_display_region_exit(VFIODisplay *dpy)
@@ -502,7 +510,7 @@ static void vfio_display_region_exit(VFIODisplay *dpy)
/* ---------------------------------------------------------------------- */
-int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
+bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
{
struct vfio_device_gfx_plane_info probe;
int ret;
@@ -525,11 +533,11 @@ int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
if (vdev->display == ON_OFF_AUTO_AUTO) {
/* not an error in automatic mode */
- return 0;
+ return true;
}
error_setg(errp, "vfio: device doesn't support any (known) display method");
- return -1;
+ return false;
}
void vfio_display_finalize(VFIOPCIDevice *vdev)
diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c
index 47b4096c05..27ea26aa48 100644
--- a/hw/vfio/helpers.c
+++ b/hw/vfio/helpers.c
@@ -107,12 +107,12 @@ static const char *index_to_str(VFIODevice *vbasedev, int index)
}
}
-int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
- int action, int fd, Error **errp)
+bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
+ int action, int fd, Error **errp)
{
ERRP_GUARD();
- struct vfio_irq_set *irq_set;
- int argsz, ret = 0;
+ g_autofree struct vfio_irq_set *irq_set = NULL;
+ int argsz;
const char *name;
int32_t *pfd;
@@ -127,16 +127,11 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
pfd = (int32_t *)&irq_set->data;
*pfd = fd;
- if (ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
- ret = -errno;
+ if (!ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
+ return true;
}
- g_free(irq_set);
- if (!ret) {
- return 0;
- }
-
- error_setg_errno(errp, -ret, "VFIO_DEVICE_SET_IRQS failure");
+ error_setg_errno(errp, errno, "VFIO_DEVICE_SET_IRQS failure");
name = index_to_str(vbasedev, index);
if (name) {
@@ -147,7 +142,7 @@ int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
error_prepend(errp,
"Failed to %s %s eventfd signaling for interrupt ",
fd < 0 ? "tear down" : "set up", action_to_str(action));
- return ret;
+ return false;
}
/*
@@ -348,7 +343,7 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
int index, const char *name)
{
- struct vfio_region_info *info;
+ g_autofree struct vfio_region_info *info = NULL;
int ret;
ret = vfio_get_region_info(vbasedev, index, &info);
@@ -381,8 +376,6 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
}
}
- g_free(info);
-
trace_vfio_region_setup(vbasedev->name, index, name,
region->flags, region->fd_offset, region->size);
return 0;
@@ -599,20 +592,19 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type,
bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type)
{
- struct vfio_region_info *info = NULL;
+ g_autofree struct vfio_region_info *info = NULL;
bool ret = false;
if (!vfio_get_region_info(vbasedev, region, &info)) {
if (vfio_get_region_info_cap(info, cap_type)) {
ret = true;
}
- g_free(info);
}
return ret;
}
-int vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
+bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
{
ERRP_GUARD();
struct stat st;
@@ -621,7 +613,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
if (stat(vbasedev->sysfsdev, &st) < 0) {
error_setg_errno(errp, errno, "no such host device");
error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev);
- return -errno;
+ return false;
}
/* User may specify a name, e.g: VFIO platform device */
if (!vbasedev->name) {
@@ -630,7 +622,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
} else {
if (!vbasedev->iommufd) {
error_setg(errp, "Use FD passing only with iommufd backend");
- return -EINVAL;
+ return false;
}
/*
* Give a name with fd so any function printing out vbasedev->name
@@ -641,7 +633,7 @@ int vfio_device_get_name(VFIODevice *vbasedev, Error **errp)
}
}
- return 0;
+ return true;
}
void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp)
diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c
index b31ee79c60..d320d032a7 100644
--- a/hw/vfio/igd.c
+++ b/hw/vfio/igd.c
@@ -367,8 +367,10 @@ static const MemoryRegionOps vfio_igd_index_quirk = {
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
{
- struct vfio_region_info *rom = NULL, *opregion = NULL,
- *host = NULL, *lpc = NULL;
+ g_autofree struct vfio_region_info *rom = NULL;
+ g_autofree struct vfio_region_info *opregion = NULL;
+ g_autofree struct vfio_region_info *host = NULL;
+ g_autofree struct vfio_region_info *lpc = NULL;
VFIOQuirk *quirk;
VFIOIGDQuirk *igd;
PCIDevice *lpc_bridge;
@@ -426,7 +428,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if ((ret || !rom->size) && !vdev->pdev.romfile) {
error_report("IGD device %s has no ROM, legacy mode disabled",
vdev->vbasedev.name);
- goto out;
+ return;
}
/*
@@ -437,7 +439,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
error_report("IGD device %s hotplugged, ROM disabled, "
"legacy mode disabled", vdev->vbasedev.name);
vdev->rom_read_failed = true;
- goto out;
+ return;
}
/*
@@ -450,7 +452,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if (ret) {
error_report("IGD device %s does not support OpRegion access,"
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
ret = vfio_get_dev_region_info(&vdev->vbasedev,
@@ -459,7 +461,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if (ret) {
error_report("IGD device %s does not support host bridge access,"
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
ret = vfio_get_dev_region_info(&vdev->vbasedev,
@@ -468,7 +470,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if (ret) {
error_report("IGD device %s does not support LPC bridge access,"
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
@@ -478,11 +480,11 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
* try to enable it. Probably shouldn't be using legacy mode without VGA,
* but also no point in us enabling VGA if disabled in hardware.
*/
- if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) {
+ if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
error_report("IGD device %s failed to enable VGA access, "
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
/* Create our LPC/ISA bridge */
@@ -490,7 +492,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if (ret) {
error_report("IGD device %s failed to create LPC bridge, "
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
/* Stuff some host values into the VM PCI host bridge */
@@ -498,15 +500,14 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
if (ret) {
error_report("IGD device %s failed to modify host bridge, "
"legacy mode disabled", vdev->vbasedev.name);
- goto out;
+ return;
}
/* Setup OpRegion access */
- ret = vfio_pci_igd_opregion_init(vdev, opregion, &err);
- if (ret) {
+ if (!vfio_pci_igd_opregion_init(vdev, opregion, &err)) {
error_append_hint(&err, "IGD legacy mode disabled\n");
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
- goto out;
+ return;
}
/* Setup our quirk to munge GTT addresses to the VM allocated buffer */
@@ -608,10 +609,4 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
}
trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb);
-
-out:
- g_free(rom);
- g_free(opregion);
- g_free(host);
- g_free(lpc);
}
diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c
index 8827ffe636..554f9a6292 100644
--- a/hw/vfio/iommufd.c
+++ b/hw/vfio/iommufd.c
@@ -49,9 +49,9 @@ static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer,
container->ioas_id, iova, size);
}
-static int iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp)
+static bool iommufd_cdev_kvm_device_add(VFIODevice *vbasedev, Error **errp)
{
- return vfio_kvm_device_add_fd(vbasedev->fd, errp);
+ return !vfio_kvm_device_add_fd(vbasedev->fd, errp);
}
static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev)
@@ -63,18 +63,16 @@ static void iommufd_cdev_kvm_device_del(VFIODevice *vbasedev)
}
}
-static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp)
+static bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp)
{
IOMMUFDBackend *iommufd = vbasedev->iommufd;
struct vfio_device_bind_iommufd bind = {
.argsz = sizeof(bind),
.flags = 0,
};
- int ret;
- ret = iommufd_backend_connect(iommufd, errp);
- if (ret) {
- return ret;
+ if (!iommufd_backend_connect(iommufd, errp)) {
+ return false;
}
/*
@@ -82,15 +80,13 @@ static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp)
* in KVM. Especially for some emulated devices, it requires
* to have kvm information in the device open.
*/
- ret = iommufd_cdev_kvm_device_add(vbasedev, errp);
- if (ret) {
+ if (!iommufd_cdev_kvm_device_add(vbasedev, errp)) {
goto err_kvm_device_add;
}
/* Bind device to iommufd */
bind.iommufd = iommufd->fd;
- ret = ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind);
- if (ret) {
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind)) {
error_setg_errno(errp, errno, "error bind device fd=%d to iommufd=%d",
vbasedev->fd, bind.iommufd);
goto err_bind;
@@ -99,12 +95,12 @@ static int iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp)
vbasedev->devid = bind.out_devid;
trace_iommufd_cdev_connect_and_bind(bind.iommufd, vbasedev->name,
vbasedev->fd, vbasedev->devid);
- return ret;
+ return true;
err_bind:
iommufd_cdev_kvm_device_del(vbasedev);
err_kvm_device_add:
iommufd_backend_disconnect(iommufd);
- return ret;
+ return false;
}
static void iommufd_cdev_unbind_and_disconnect(VFIODevice *vbasedev)
@@ -176,10 +172,10 @@ out:
return ret;
}
-static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id,
+static bool iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id,
Error **errp)
{
- int ret, iommufd = vbasedev->iommufd->fd;
+ int iommufd = vbasedev->iommufd->fd;
struct vfio_device_attach_iommufd_pt attach_data = {
.argsz = sizeof(attach_data),
.flags = 0,
@@ -187,38 +183,38 @@ static int iommufd_cdev_attach_ioas_hwpt(VFIODevice *vbasedev, uint32_t id,
};
/* Attach device to an IOAS or hwpt within iommufd */
- ret = ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data);
- if (ret) {
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data)) {
error_setg_errno(errp, errno,
"[iommufd=%d] error attach %s (%d) to id=%d",
iommufd, vbasedev->name, vbasedev->fd, id);
- } else {
- trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name,
- vbasedev->fd, id);
+ return false;
}
- return ret;
+
+ trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name,
+ vbasedev->fd, id);
+ return true;
}
-static int iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp)
+static bool iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp)
{
- int ret, iommufd = vbasedev->iommufd->fd;
+ int iommufd = vbasedev->iommufd->fd;
struct vfio_device_detach_iommufd_pt detach_data = {
.argsz = sizeof(detach_data),
.flags = 0,
};
- ret = ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data);
- if (ret) {
+ if (ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data)) {
error_setg_errno(errp, errno, "detach %s failed", vbasedev->name);
- } else {
- trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name);
+ return false;
}
- return ret;
+
+ trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name);
+ return true;
}
-static int iommufd_cdev_attach_container(VFIODevice *vbasedev,
- VFIOIOMMUFDContainer *container,
- Error **errp)
+static bool iommufd_cdev_attach_container(VFIODevice *vbasedev,
+ VFIOIOMMUFDContainer *container,
+ Error **errp)
{
return iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp);
}
@@ -228,7 +224,7 @@ static void iommufd_cdev_detach_container(VFIODevice *vbasedev,
{
Error *err = NULL;
- if (iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) {
+ if (!iommufd_cdev_detach_ioas_hwpt(vbasedev, &err)) {
error_report_err(err);
}
}
@@ -254,20 +250,19 @@ static int iommufd_cdev_ram_block_discard_disable(bool state)
return ram_block_uncoordinated_discard_disable(state);
}
-static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container,
- uint32_t ioas_id, Error **errp)
+static bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container,
+ uint32_t ioas_id, Error **errp)
{
VFIOContainerBase *bcontainer = &container->bcontainer;
- struct iommu_ioas_iova_ranges *info;
+ g_autofree struct iommu_ioas_iova_ranges *info = NULL;
struct iommu_iova_range *iova_ranges;
- int ret, sz, fd = container->be->fd;
+ int sz, fd = container->be->fd;
info = g_malloc0(sizeof(*info));
info->size = sizeof(*info);
info->ioas_id = ioas_id;
- ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info);
- if (ret && errno != EMSGSIZE) {
+ if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info) && errno != EMSGSIZE) {
goto error;
}
@@ -275,8 +270,7 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container,
info = g_realloc(info, sizeof(*info) + sz);
info->allowed_iovas = (uintptr_t)(info + 1);
- ret = ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info);
- if (ret) {
+ if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info)) {
goto error;
}
@@ -291,18 +285,15 @@ static int iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container,
}
bcontainer->pgsizes = info->out_iova_alignment;
- g_free(info);
- return 0;
+ return true;
error:
- ret = -errno;
- g_free(info);
error_setg_errno(errp, errno, "Cannot get IOVA ranges");
- return ret;
+ return false;
}
-static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
- AddressSpace *as, Error **errp)
+static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
+ AddressSpace *as, Error **errp)
{
VFIOContainerBase *bcontainer;
VFIOIOMMUFDContainer *container;
@@ -317,15 +308,14 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
if (vbasedev->fd < 0) {
devfd = iommufd_cdev_getfd(vbasedev->sysfsdev, errp);
if (devfd < 0) {
- return devfd;
+ return false;
}
vbasedev->fd = devfd;
} else {
devfd = vbasedev->fd;
}
- ret = iommufd_cdev_connect_and_bind(vbasedev, errp);
- if (ret) {
+ if (!iommufd_cdev_connect_and_bind(vbasedev, errp)) {
goto err_connect_bind;
}
@@ -338,7 +328,7 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
vbasedev->iommufd != container->be) {
continue;
}
- if (iommufd_cdev_attach_container(vbasedev, container, &err)) {
+ if (!iommufd_cdev_attach_container(vbasedev, container, &err)) {
const char *msg = error_get_pretty(err);
trace_iommufd_cdev_fail_attach_existing_container(msg);
@@ -356,8 +346,7 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
}
/* Need to allocate a new dedicated container */
- ret = iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp);
- if (ret < 0) {
+ if (!iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp)) {
goto err_alloc_ioas;
}
@@ -371,8 +360,7 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
vfio_container_init(bcontainer, space, iommufd_vioc);
QLIST_INSERT_HEAD(&space->containers, bcontainer, next);
- ret = iommufd_cdev_attach_container(vbasedev, container, errp);
- if (ret) {
+ if (!iommufd_cdev_attach_container(vbasedev, container, errp)) {
goto err_attach_container;
}
@@ -381,8 +369,7 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
goto err_discard_disable;
}
- ret = iommufd_cdev_get_info_iova_range(container, ioas_id, &err);
- if (ret) {
+ if (!iommufd_cdev_get_info_iova_range(container, ioas_id, &err)) {
error_append_hint(&err,
"Fallback to default 64bit IOVA range and 4K page size\n");
warn_report_err(err);
@@ -394,7 +381,6 @@ static int iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
memory_listener_register(&bcontainer->listener, bcontainer->space->as);
if (bcontainer->error) {
- ret = -1;
error_propagate_prepend(errp, bcontainer->error,
"memory listener initialization failed: ");
goto err_listener_register;
@@ -409,8 +395,7 @@ found_container:
goto err_listener_register;
}
- ret = vfio_cpr_register_container(bcontainer, errp);
- if (ret) {
+ if (!vfio_cpr_register_container(bcontainer, errp)) {
goto err_listener_register;
}
@@ -433,7 +418,7 @@ found_container:
trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs,
vbasedev->num_regions, vbasedev->flags);
- return 0;
+ return true;
err_listener_register:
iommufd_cdev_ram_block_discard_disable(false);
@@ -446,7 +431,7 @@ err_alloc_ioas:
iommufd_cdev_unbind_and_disconnect(vbasedev);
err_connect_bind:
close(vbasedev->fd);
- return ret;
+ return false;
}
static void iommufd_cdev_detach(VFIODevice *vbasedev)
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 06ae40969b..34d4be2ce1 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -24,6 +24,7 @@
#include "migration/register.h"
#include "migration/blocker.h"
#include "qapi/error.h"
+#include "qapi/qapi-events-vfio.h"
#include "exec/ramlist.h"
#include "exec/ram_addr.h"
#include "pci.h"
@@ -80,9 +81,65 @@ static const char *mig_state_to_str(enum vfio_device_mig_state state)
}
}
+static VfioMigrationState
+mig_state_to_qapi_state(enum vfio_device_mig_state state)
+{
+ switch (state) {
+ case VFIO_DEVICE_STATE_STOP:
+ return QAPI_VFIO_MIGRATION_STATE_STOP;
+ case VFIO_DEVICE_STATE_RUNNING:
+ return QAPI_VFIO_MIGRATION_STATE_RUNNING;
+ case VFIO_DEVICE_STATE_STOP_COPY:
+ return QAPI_VFIO_MIGRATION_STATE_STOP_COPY;
+ case VFIO_DEVICE_STATE_RESUMING:
+ return QAPI_VFIO_MIGRATION_STATE_RESUMING;
+ case VFIO_DEVICE_STATE_RUNNING_P2P:
+ return QAPI_VFIO_MIGRATION_STATE_RUNNING_P2P;
+ case VFIO_DEVICE_STATE_PRE_COPY:
+ return QAPI_VFIO_MIGRATION_STATE_PRE_COPY;
+ case VFIO_DEVICE_STATE_PRE_COPY_P2P:
+ return QAPI_VFIO_MIGRATION_STATE_PRE_COPY_P2P;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void vfio_migration_send_event(VFIODevice *vbasedev)
+{
+ VFIOMigration *migration = vbasedev->migration;
+ DeviceState *dev = vbasedev->dev;
+ g_autofree char *qom_path = NULL;
+ Object *obj;
+
+ if (!vbasedev->migration_events) {
+ return;
+ }
+
+ g_assert(vbasedev->ops->vfio_get_object);
+ obj = vbasedev->ops->vfio_get_object(vbasedev);
+ g_assert(obj);
+ qom_path = object_get_canonical_path(obj);
+
+ qapi_event_send_vfio_migration(
+ dev->id, qom_path, mig_state_to_qapi_state(migration->device_state));
+}
+
+static void vfio_migration_set_device_state(VFIODevice *vbasedev,
+ enum vfio_device_mig_state state)
+{
+ VFIOMigration *migration = vbasedev->migration;
+
+ trace_vfio_migration_set_device_state(vbasedev->name,
+ mig_state_to_str(state));
+
+ migration->device_state = state;
+ vfio_migration_send_event(vbasedev);
+}
+
static int vfio_migration_set_state(VFIODevice *vbasedev,
enum vfio_device_mig_state new_state,
- enum vfio_device_mig_state recover_state)
+ enum vfio_device_mig_state recover_state,
+ Error **errp)
{
VFIOMigration *migration = vbasedev->migration;
uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) +
@@ -92,6 +149,16 @@ static int vfio_migration_set_state(VFIODevice *vbasedev,
struct vfio_device_feature_mig_state *mig_state =
(struct vfio_device_feature_mig_state *)feature->data;
int ret;
+ g_autofree char *error_prefix =
+ g_strdup_printf("%s: Failed setting device state to %s.",
+ vbasedev->name, mig_state_to_str(new_state));
+
+ trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state),
+ mig_state_to_str(recover_state));
+
+ if (new_state == migration->device_state) {
+ return 0;
+ }
feature->argsz = sizeof(buf);
feature->flags =
@@ -102,22 +169,24 @@ static int vfio_migration_set_state(VFIODevice *vbasedev,
ret = -errno;
if (recover_state == VFIO_DEVICE_STATE_ERROR) {
- error_report("%s: Failed setting device state to %s, err: %s. "
- "Recover state is ERROR. Resetting device",
- vbasedev->name, mig_state_to_str(new_state),
- strerror(errno));
+ error_setg_errno(errp, errno,
+ "%s Recover state is ERROR. Resetting device",
+ error_prefix);
goto reset_device;
}
- error_report(
- "%s: Failed setting device state to %s, err: %s. Setting device in recover state %s",
- vbasedev->name, mig_state_to_str(new_state),
- strerror(errno), mig_state_to_str(recover_state));
+ error_setg_errno(errp, errno,
+ "%s Setting device in recover state %s",
+ error_prefix, mig_state_to_str(recover_state));
mig_state->device_state = recover_state;
if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
ret = -errno;
+ /*
+ * If setting the device in recover state fails, report
+ * the error here and propagate the first error.
+ */
error_report(
"%s: Failed setting device in recover state, err: %s. Resetting device",
vbasedev->name, strerror(errno));
@@ -125,19 +194,19 @@ static int vfio_migration_set_state(VFIODevice *vbasedev,
goto reset_device;
}
- migration->device_state = recover_state;
+ vfio_migration_set_device_state(vbasedev, recover_state);
return ret;
}
- migration->device_state = new_state;
+ vfio_migration_set_device_state(vbasedev, new_state);
if (mig_state->data_fd != -1) {
if (migration->data_fd != -1) {
/*
* This can happen if the device is asynchronously reset and
* terminates a data transfer.
*/
- error_report("%s: data_fd out of sync", vbasedev->name);
+ error_setg(errp, "%s: data_fd out of sync", vbasedev->name);
close(mig_state->data_fd);
return -EBADF;
@@ -146,8 +215,6 @@ static int vfio_migration_set_state(VFIODevice *vbasedev,
migration->data_fd = mig_state->data_fd;
}
- trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state));
-
return 0;
reset_device:
@@ -156,7 +223,7 @@ reset_device:
strerror(errno));
}
- migration->device_state = VFIO_DEVICE_STATE_RUNNING;
+ vfio_migration_set_device_state(vbasedev, VFIO_DEVICE_STATE_RUNNING);
return ret;
}
@@ -168,10 +235,11 @@ reset_device:
*/
static int
vfio_migration_set_state_or_reset(VFIODevice *vbasedev,
- enum vfio_device_mig_state new_state)
+ enum vfio_device_mig_state new_state,
+ Error **errp)
{
return vfio_migration_set_state(vbasedev, new_state,
- VFIO_DEVICE_STATE_ERROR);
+ VFIO_DEVICE_STATE_ERROR, errp);
}
static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
@@ -186,21 +254,30 @@ static int vfio_load_buffer(QEMUFile *f, VFIODevice *vbasedev,
return ret;
}
-static int vfio_save_device_config_state(QEMUFile *f, void *opaque)
+static int vfio_save_device_config_state(QEMUFile *f, void *opaque,
+ Error **errp)
{
VFIODevice *vbasedev = opaque;
+ int ret;
qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_STATE);
if (vbasedev->ops && vbasedev->ops->vfio_save_config) {
- vbasedev->ops->vfio_save_config(vbasedev, f);
+ ret = vbasedev->ops->vfio_save_config(vbasedev, f, errp);
+ if (ret) {
+ return ret;
+ }
}
qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
trace_vfio_save_device_config_state(vbasedev->name);
- return qemu_file_get_error(f);
+ ret = qemu_file_get_error(f);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to save state");
+ }
+ return ret;
}
static int vfio_load_device_config_state(QEMUFile *f, void *opaque)
@@ -399,10 +476,8 @@ static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp)
switch (migration->device_state) {
case VFIO_DEVICE_STATE_RUNNING:
ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY,
- VFIO_DEVICE_STATE_RUNNING);
+ VFIO_DEVICE_STATE_RUNNING, errp);
if (ret) {
- error_setg(errp, "%s: Failed to set new PRE_COPY state",
- vbasedev->name);
return ret;
}
@@ -435,13 +510,20 @@ static void vfio_save_cleanup(void *opaque)
{
VFIODevice *vbasedev = opaque;
VFIOMigration *migration = vbasedev->migration;
+ Error *local_err = NULL;
+ int ret;
/*
* Changing device state from STOP_COPY to STOP can take time. Do it here,
* after migration has completed, so it won't increase downtime.
*/
if (migration->device_state == VFIO_DEVICE_STATE_STOP_COPY) {
- vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_STOP);
+ ret = vfio_migration_set_state_or_reset(vbasedev,
+ VFIO_DEVICE_STATE_STOP,
+ &local_err);
+ if (ret) {
+ error_report_err(local_err);
+ }
}
g_free(migration->data_buffer);
@@ -549,11 +631,13 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
VFIODevice *vbasedev = opaque;
ssize_t data_size;
int ret;
+ Error *local_err = NULL;
/* We reach here with device state STOP or STOP_COPY only */
ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
- VFIO_DEVICE_STATE_STOP);
+ VFIO_DEVICE_STATE_STOP, &local_err);
if (ret) {
+ error_report_err(local_err);
return ret;
}
@@ -566,9 +650,6 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
ret = qemu_file_get_error(f);
- if (ret) {
- return ret;
- }
trace_vfio_save_complete_precopy(vbasedev->name, ret);
@@ -578,27 +659,24 @@ static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
static void vfio_save_state(QEMUFile *f, void *opaque)
{
VFIODevice *vbasedev = opaque;
+ Error *local_err = NULL;
int ret;
- ret = vfio_save_device_config_state(f, opaque);
+ ret = vfio_save_device_config_state(f, opaque, &local_err);
if (ret) {
- error_report("%s: Failed to save device config space",
- vbasedev->name);
- qemu_file_set_error(f, ret);
+ error_prepend(&local_err,
+ "vfio: Failed to save device config space of %s - ",
+ vbasedev->name);
+ qemu_file_set_error_obj(f, ret, local_err);
}
}
static int vfio_load_setup(QEMUFile *f, void *opaque, Error **errp)
{
VFIODevice *vbasedev = opaque;
- int ret;
- ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING,
- vbasedev->migration->device_state);
- if (ret) {
- error_setg(errp, "%s: Failed to set RESUMING state", vbasedev->name);
- }
- return ret;
+ return vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_RESUMING,
+ vbasedev->migration->device_state, errp);
}
static int vfio_load_cleanup(void *opaque)
@@ -714,19 +792,20 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
VFIODevice *vbasedev = opaque;
VFIOMigration *migration = vbasedev->migration;
enum vfio_device_mig_state new_state;
+ Error *local_err = NULL;
int ret;
new_state = migration->device_state == VFIO_DEVICE_STATE_PRE_COPY ?
VFIO_DEVICE_STATE_PRE_COPY_P2P :
VFIO_DEVICE_STATE_RUNNING_P2P;
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state);
+ ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
* currently does not support reporting failures.
*/
- migration_file_set_error(ret);
+ migration_file_set_error(ret, local_err);
}
trace_vfio_vmstate_change_prepare(vbasedev->name, running,
@@ -738,6 +817,7 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
{
VFIODevice *vbasedev = opaque;
enum vfio_device_mig_state new_state;
+ Error *local_err = NULL;
int ret;
if (running) {
@@ -750,13 +830,13 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
VFIO_DEVICE_STATE_STOP;
}
- ret = vfio_migration_set_state_or_reset(vbasedev, new_state);
+ ret = vfio_migration_set_state_or_reset(vbasedev, new_state, &local_err);
if (ret) {
/*
* Migration should be aborted in this case, but vm_state_notify()
* currently does not support reporting failures.
*/
- migration_file_set_error(ret);
+ migration_file_set_error(ret, local_err);
}
trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state),
@@ -769,11 +849,23 @@ static int vfio_migration_state_notifier(NotifierWithReturn *notifier,
VFIOMigration *migration = container_of(notifier, VFIOMigration,
migration_state);
VFIODevice *vbasedev = migration->vbasedev;
+ Error *local_err = NULL;
+ int ret;
trace_vfio_migration_state_notifier(vbasedev->name, e->type);
if (e->type == MIG_EVENT_PRECOPY_FAILED) {
- vfio_migration_set_state_or_reset(vbasedev, VFIO_DEVICE_STATE_RUNNING);
+ /*
+ * MigrationNotifyFunc may not return an error code and an Error
+ * object for MIG_EVENT_PRECOPY_FAILED. Hence, report the error
+ * locally and ignore the errp argument.
+ */
+ ret = vfio_migration_set_state_or_reset(vbasedev,
+ VFIO_DEVICE_STATE_RUNNING,
+ &local_err);
+ if (ret) {
+ error_report_err(local_err);
+ }
}
return 0;
}
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index 496fd1ee86..39dae72497 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -1169,8 +1169,8 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
* the table and to write the base address of that memory to the ASLS register
* of the IGD device.
*/
-int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
- struct vfio_region_info *info, Error **errp)
+bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info, Error **errp)
{
int ret;
@@ -1181,7 +1181,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
error_setg(errp, "failed to read IGD OpRegion");
g_free(vdev->igd_opregion);
vdev->igd_opregion = NULL;
- return -EINVAL;
+ return false;
}
/*
@@ -1206,7 +1206,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0);
pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0);
- return 0;
+ return true;
}
/*
@@ -1536,7 +1536,7 @@ static bool is_valid_std_cap_offset(uint8_t pos)
pos <= (PCI_CFG_SPACE_SIZE - PCI_CAP_SIZEOF));
}
-static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
{
ERRP_GUARD();
PCIDevice *pdev = &vdev->pdev;
@@ -1545,18 +1545,18 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
uint8_t tmp;
if (vdev->nv_gpudirect_clique == 0xFF) {
- return 0;
+ return true;
}
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID)) {
error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid device vendor");
- return -EINVAL;
+ return false;
}
if (pci_get_byte(pdev->config + PCI_CLASS_DEVICE + 1) !=
PCI_BASE_CLASS_DISPLAY) {
error_setg(errp, "NVIDIA GPUDirect Clique ID: unsupported PCI class");
- return -EINVAL;
+ return false;
}
/*
@@ -1572,7 +1572,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
vdev->config_offset + PCI_CAPABILITY_LIST);
if (ret != 1 || !is_valid_std_cap_offset(tmp)) {
error_setg(errp, "NVIDIA GPUDirect Clique ID: error getting cap list");
- return -EINVAL;
+ return false;
}
do {
@@ -1590,13 +1590,13 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
pos = 0xD4;
} else {
error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid config space");
- return -EINVAL;
+ return false;
}
ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, 8, errp);
if (ret < 0) {
error_prepend(errp, "Failed to add NVIDIA GPUDirect cap: ");
- return ret;
+ return false;
}
memset(vdev->emulated_config_bits + pos, 0xFF, 8);
@@ -1608,7 +1608,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
pci_set_byte(pdev->config + pos++, vdev->nv_gpudirect_clique << 3);
pci_set_byte(pdev->config + pos, 0);
- return 0;
+ return true;
}
/*
@@ -1629,7 +1629,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp)
*/
#define VMD_SHADOW_CAP_VER 1
#define VMD_SHADOW_CAP_LEN 24
-static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp)
{
ERRP_GUARD();
uint8_t membar_phys[16];
@@ -1639,7 +1639,7 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp)
vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x467F) ||
vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x4C3D) ||
vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x9A0B))) {
- return 0;
+ return true;
}
ret = pread(vdev->vbasedev.fd, membar_phys, 16,
@@ -1647,14 +1647,14 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp)
if (ret != 16) {
error_report("VMD %s cannot read MEMBARs (%d)",
vdev->vbasedev.name, ret);
- return -EFAULT;
+ return false;
}
ret = pci_add_capability(&vdev->pdev, PCI_CAP_ID_VNDR, pos,
VMD_SHADOW_CAP_LEN, errp);
if (ret < 0) {
error_prepend(errp, "Failed to add VMD MEMBAR Shadow cap: ");
- return ret;
+ return false;
}
memset(vdev->emulated_config_bits + pos, 0xFF, VMD_SHADOW_CAP_LEN);
@@ -1664,22 +1664,18 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp)
pci_set_long(vdev->pdev.config + pos, 0x53484457); /* SHDW */
memcpy(vdev->pdev.config + pos + 4, membar_phys, 16);
- return 0;
+ return true;
}
-int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp)
+bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp)
{
- int ret;
-
- ret = vfio_add_nv_gpudirect_cap(vdev, errp);
- if (ret) {
- return ret;
+ if (!vfio_add_nv_gpudirect_cap(vdev, errp)) {
+ return false;
}
- ret = vfio_add_vmd_shadow_cap(vdev, errp);
- if (ret) {
- return ret;
+ if (!vfio_add_vmd_shadow_cap(vdev, errp)) {
+ return false;
}
- return 0;
+ return true;
}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 64780d1b79..74a79bdf61 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -116,7 +116,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev)
vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
}
-static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
{
#ifdef CONFIG_KVM
int irq_fd = event_notifier_get_fd(&vdev->intx.interrupt);
@@ -124,7 +124,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
if (vdev->no_kvm_intx || !kvm_irqfds_enabled() ||
vdev->intx.route.mode != PCI_INTX_ENABLED ||
!kvm_resamplefds_enabled()) {
- return;
+ return true;
}
/* Get to a known interrupt state */
@@ -147,10 +147,10 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
goto fail_irqfd;
}
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_UNMASK,
- event_notifier_get_fd(&vdev->intx.unmask),
- errp)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_UNMASK,
+ event_notifier_get_fd(&vdev->intx.unmask),
+ errp)) {
goto fail_vfio;
}
@@ -161,7 +161,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
trace_vfio_intx_enable_kvm(vdev->vbasedev.name);
- return;
+ return true;
fail_vfio:
kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt,
@@ -171,6 +171,9 @@ fail_irqfd:
fail:
qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev);
vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
+ return false;
+#else
+ return true;
#endif
}
@@ -226,8 +229,7 @@ static void vfio_intx_update(VFIOPCIDevice *vdev, PCIINTxRoute *route)
return;
}
- vfio_intx_enable_kvm(vdev, &err);
- if (err) {
+ if (!vfio_intx_enable_kvm(vdev, &err)) {
warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
@@ -259,7 +261,7 @@ static void vfio_irqchip_change(Notifier *notify, void *data)
vfio_intx_update(vdev, &vdev->intx.route);
}
-static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp)
{
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
Error *err = NULL;
@@ -268,7 +270,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp)
if (!pin) {
- return 0;
+ return true;
}
vfio_disable_interrupts(vdev);
@@ -290,27 +292,26 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp)
ret = event_notifier_init(&vdev->intx.interrupt, 0);
if (ret) {
error_setg_errno(errp, -ret, "event_notifier_init failed");
- return ret;
+ return false;
}
fd = event_notifier_get_fd(&vdev->intx.interrupt);
qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev);
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) {
qemu_set_fd_handler(fd, NULL, NULL, vdev);
event_notifier_cleanup(&vdev->intx.interrupt);
- return -errno;
+ return false;
}
- vfio_intx_enable_kvm(vdev, &err);
- if (err) {
+ if (!vfio_intx_enable_kvm(vdev, &err)) {
warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
vdev->interrupt = VFIO_INT_INTx;
trace_vfio_intx_enable(vdev->vbasedev.name);
- return 0;
+ return true;
}
static void vfio_intx_disable(VFIOPCIDevice *vdev)
@@ -590,9 +591,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
fd = event_notifier_get_fd(&vector->interrupt);
}
- if (vfio_set_irq_signaling(&vdev->vbasedev,
- VFIO_PCI_MSIX_IRQ_INDEX, nr,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev,
+ VFIO_PCI_MSIX_IRQ_INDEX, nr,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd,
+ &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
}
@@ -634,8 +636,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
int32_t fd = event_notifier_get_fd(&vector->interrupt);
Error *err = NULL;
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX,
+ nr, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
+ &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
}
@@ -833,8 +836,7 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev)
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX);
vfio_msi_disable_common(vdev);
- vfio_intx_enable(vdev, &err);
- if (err) {
+ if (!vfio_intx_enable(vdev, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
@@ -877,7 +879,7 @@ static void vfio_update_msi(VFIOPCIDevice *vdev)
static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
{
- struct vfio_region_info *reg_info;
+ g_autofree struct vfio_region_info *reg_info = NULL;
uint64_t size;
off_t off = 0;
ssize_t bytes;
@@ -895,8 +897,6 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
vdev->rom_size = size = reg_info->size;
vdev->rom_offset = reg_info->offset;
- g_free(reg_info);
-
if (!vdev->rom_size) {
vdev->rom_read_failed = true;
error_report("vfio-pci: Cannot read device rom at "
@@ -1337,7 +1337,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev)
}
}
-static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
+static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
{
uint16_t ctrl;
bool msi_64bit, msi_maskbit;
@@ -1347,7 +1347,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl),
vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
error_setg_errno(errp, errno, "failed reading MSI PCI_CAP_FLAGS");
- return -errno;
+ return false;
}
ctrl = le16_to_cpu(ctrl);
@@ -1360,14 +1360,14 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err);
if (ret < 0) {
if (ret == -ENOTSUP) {
- return 0;
+ return true;
}
error_propagate_prepend(errp, err, "msi_init failed: ");
- return ret;
+ return false;
}
vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
- return 0;
+ return true;
}
static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
@@ -1447,13 +1447,13 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
}
}
-static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
{
int target_bar = -1;
size_t msix_sz;
if (!vdev->msix || vdev->msix_relo == OFF_AUTOPCIBAR_OFF) {
- return;
+ return true;
}
/* The actual minimum size of MSI-X structures */
@@ -1476,7 +1476,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
if (target_bar < 0) {
error_setg(errp, "No automatic MSI-X relocation available for "
"device %04x:%04x", vdev->vendor_id, vdev->device_id);
- return;
+ return false;
}
} else {
target_bar = (int)(vdev->msix_relo - OFF_AUTOPCIBAR_BAR0);
@@ -1486,7 +1486,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
if (vdev->bars[target_bar].ioport) {
error_setg(errp, "Invalid MSI-X relocation BAR %d, "
"I/O port BAR", target_bar);
- return;
+ return false;
}
/* Cannot use a BAR in the "shadow" of a 64-bit BAR */
@@ -1494,7 +1494,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
target_bar > 0 && vdev->bars[target_bar - 1].mem64) {
error_setg(errp, "Invalid MSI-X relocation BAR %d, "
"consumed by 64-bit BAR %d", target_bar, target_bar - 1);
- return;
+ return false;
}
/* 2GB max size for 32-bit BARs, cannot double if already > 1G */
@@ -1502,7 +1502,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
!vdev->bars[target_bar].mem64) {
error_setg(errp, "Invalid MSI-X relocation BAR %d, "
"no space to extend 32-bit BAR", target_bar);
- return;
+ return false;
}
/*
@@ -1537,6 +1537,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
trace_vfio_msix_relo(vdev->vbasedev.name,
vdev->msix->table_bar, vdev->msix->table_offset);
+ return true;
}
/*
@@ -1547,7 +1548,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp)
* need to first look for where the MSI-X table lives. So we
* unfortunately split MSI-X setup across two functions.
*/
-static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
{
uint8_t pos;
uint16_t ctrl;
@@ -1559,25 +1560,25 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
if (!pos) {
- return;
+ return true;
}
if (pread(fd, &ctrl, sizeof(ctrl),
vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) {
error_setg_errno(errp, errno, "failed to read PCI MSIX FLAGS");
- return;
+ return false;
}
if (pread(fd, &table, sizeof(table),
vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) {
error_setg_errno(errp, errno, "failed to read PCI MSIX TABLE");
- return;
+ return false;
}
if (pread(fd, &pba, sizeof(pba),
vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) {
error_setg_errno(errp, errno, "failed to read PCI MSIX PBA");
- return;
+ return false;
}
ctrl = le16_to_cpu(ctrl);
@@ -1595,7 +1596,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
if (ret < 0) {
error_setg_errno(errp, -ret, "failed to get MSI-X irq info");
g_free(msix);
- return;
+ return false;
}
msix->noresize = !!(irq_info.flags & VFIO_IRQ_INFO_NORESIZE);
@@ -1627,7 +1628,7 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
error_setg(errp, "hardware reports invalid configuration, "
"MSIX PBA outside of specified BAR");
g_free(msix);
- return;
+ return false;
}
}
@@ -1638,10 +1639,10 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
vfio_pci_fixup_msix_region(vdev);
- vfio_pci_relocate_msix(vdev, errp);
+ return vfio_pci_relocate_msix(vdev, errp);
}
-static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
+static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
{
int ret;
Error *err = NULL;
@@ -1657,11 +1658,11 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
if (ret < 0) {
if (ret == -ENOTSUP) {
warn_report_err(err);
- return 0;
+ return true;
}
error_propagate(errp, err);
- return ret;
+ return false;
}
/*
@@ -1695,7 +1696,7 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false);
}
- return 0;
+ return true;
}
static void vfio_teardown_msi(VFIOPCIDevice *vdev)
@@ -1974,8 +1975,8 @@ static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev)
}
}
-static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
- Error **errp)
+static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
+ Error **errp)
{
uint16_t flags;
uint8_t type;
@@ -1989,7 +1990,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
error_setg(errp, "assignment of PCIe type 0x%x "
"devices is not currently supported", type);
- return -EINVAL;
+ return false;
}
if (!pci_bus_is_express(pci_get_bus(&vdev->pdev))) {
@@ -2022,7 +2023,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
}
if (pci_bus_is_express(bus)) {
- return 0;
+ return true;
}
} else if (pci_bus_is_root(pci_get_bus(&vdev->pdev))) {
@@ -2060,7 +2061,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
* Legacy endpoints don't belong on the root complex. Windows
* seems to be happier with devices if we skip the capability.
*/
- return 0;
+ return true;
}
} else {
@@ -2096,12 +2097,12 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size,
errp);
if (pos < 0) {
- return pos;
+ return false;
}
vdev->pdev.exp.exp_cap = pos;
- return pos;
+ return true;
}
static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos)
@@ -2134,12 +2135,34 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos)
}
}
-static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
+static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos,
+ uint8_t size, Error **errp)
+{
+ PCIDevice *pdev = &vdev->pdev;
+
+ pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp);
+ if (pos < 0) {
+ return false;
+ }
+
+ /*
+ * Exempt config space check for Vendor Specific Information during
+ * restore/load.
+ * Config space check is still enforced for 3 byte VSC header.
+ */
+ if (vdev->skip_vsc_check && size > 3) {
+ memset(pdev->cmask + pos + 3, 0, size - 3);
+ }
+
+ return true;
+}
+
+static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
{
ERRP_GUARD();
PCIDevice *pdev = &vdev->pdev;
uint8_t cap_id, next, size;
- int ret;
+ bool ret;
cap_id = pdev->config[pos];
next = pdev->config[pos + PCI_CAP_LIST_NEXT];
@@ -2160,9 +2183,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
* will be changed as we unwind the stack.
*/
if (next) {
- ret = vfio_add_std_cap(vdev, next, errp);
- if (ret) {
- return ret;
+ if (!vfio_add_std_cap(vdev, next, errp)) {
+ return false;
}
} else {
/* Begin the rebuild, use QEMU emulated list bits */
@@ -2170,9 +2192,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff;
vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
- ret = vfio_add_virt_caps(vdev, errp);
- if (ret) {
- return ret;
+ if (!vfio_add_virt_caps(vdev, errp)) {
+ return false;
}
}
@@ -2196,25 +2217,27 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
case PCI_CAP_ID_PM:
vfio_check_pm_reset(vdev, pos);
vdev->pm_cap = pos;
- ret = pci_add_capability(pdev, cap_id, pos, size, errp);
+ ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0;
break;
case PCI_CAP_ID_AF:
vfio_check_af_flr(vdev, pos);
- ret = pci_add_capability(pdev, cap_id, pos, size, errp);
+ ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0;
+ break;
+ case PCI_CAP_ID_VNDR:
+ ret = vfio_add_vendor_specific_cap(vdev, pos, size, errp);
break;
default:
- ret = pci_add_capability(pdev, cap_id, pos, size, errp);
+ ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0;
break;
}
- if (ret < 0) {
+ if (!ret) {
error_prepend(errp,
"failed to add PCI capability 0x%x[0x%x]@0x%x: ",
cap_id, size, pos);
- return ret;
}
- return 0;
+ return ret;
}
static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos)
@@ -2360,23 +2383,21 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev)
return;
}
-static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp)
{
PCIDevice *pdev = &vdev->pdev;
- int ret;
if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) ||
!pdev->config[PCI_CAPABILITY_LIST]) {
- return 0; /* Nothing to add */
+ return true; /* Nothing to add */
}
- ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp);
- if (ret) {
- return ret;
+ if (!vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp)) {
+ return false;
}
vfio_add_ext_cap(vdev);
- return 0;
+ return true;
}
void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
@@ -2421,8 +2442,7 @@ void vfio_pci_post_reset(VFIOPCIDevice *vdev)
Error *err = NULL;
int nr;
- vfio_intx_enable(vdev, &err);
- if (err) {
+ if (!vfio_intx_enable(vdev, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
@@ -2586,11 +2606,12 @@ static const VMStateDescription vmstate_vfio_pci_config = {
}
};
-static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f)
+static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp)
{
VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
- vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL);
+ return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev, NULL,
+ errp);
}
static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
@@ -2642,10 +2663,10 @@ static VFIODeviceOps vfio_pci_ops = {
.vfio_load_config = vfio_pci_load_config,
};
-int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
+bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
{
VFIODevice *vbasedev = &vdev->vbasedev;
- struct vfio_region_info *reg_info;
+ g_autofree struct vfio_region_info *reg_info = NULL;
int ret;
ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, &reg_info);
@@ -2653,7 +2674,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
error_setg_errno(errp, -ret,
"failed getting region info for VGA region index %d",
VFIO_PCI_VGA_REGION_INDEX);
- return ret;
+ return false;
}
if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
@@ -2662,8 +2683,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
error_setg(errp, "unexpected VGA info, flags 0x%lx, size 0x%lx",
(unsigned long)reg_info->flags,
(unsigned long)reg_info->size);
- g_free(reg_info);
- return -EINVAL;
+ return false;
}
vdev->vga = g_new0(VFIOVGA, 1);
@@ -2671,8 +2691,6 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
vdev->vga->fd_offset = reg_info->offset;
vdev->vga->fd = vdev->vbasedev.fd;
- g_free(reg_info);
-
vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE;
vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM;
QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks);
@@ -2707,31 +2725,31 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
&vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
&vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
- return 0;
+ return true;
}
-static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
+static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
{
VFIODevice *vbasedev = &vdev->vbasedev;
- struct vfio_region_info *reg_info;
+ g_autofree struct vfio_region_info *reg_info = NULL;
struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) };
int i, ret = -1;
/* Sanity check device */
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) {
error_setg(errp, "this isn't a PCI device");
- return;
+ return false;
}
if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) {
error_setg(errp, "unexpected number of io regions %u",
vbasedev->num_regions);
- return;
+ return false;
}
if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) {
error_setg(errp, "unexpected number of irqs %u", vbasedev->num_irqs);
- return;
+ return false;
}
for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
@@ -2743,7 +2761,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
if (ret) {
error_setg_errno(errp, -ret, "failed to get region %d info", i);
- return;
+ return false;
}
QLIST_INIT(&vdev->bars[i].quirks);
@@ -2753,7 +2771,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
VFIO_PCI_CONFIG_REGION_INDEX, &reg_info);
if (ret) {
error_setg_errno(errp, -ret, "failed to get config info");
- return;
+ return false;
}
trace_vfio_populate_device_config(vdev->vbasedev.name,
@@ -2767,14 +2785,11 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
}
vdev->config_offset = reg_info->offset;
- g_free(reg_info);
-
if (vdev->features & VFIO_FEATURE_ENABLE_VGA) {
- ret = vfio_populate_vga(vdev, errp);
- if (ret) {
+ if (!vfio_populate_vga(vdev, errp)) {
error_append_hint(errp, "device does not support "
"requested feature x-vga\n");
- return;
+ return false;
}
}
@@ -2791,6 +2806,8 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
"Could not enable error recovery for the device",
vbasedev->name);
}
+
+ return true;
}
static void vfio_pci_put_device(VFIOPCIDevice *vdev)
@@ -2847,8 +2864,8 @@ static void vfio_register_err_notifier(VFIOPCIDevice *vdev)
fd = event_notifier_get_fd(&vdev->err_notifier);
qemu_set_fd_handler(fd, vfio_err_notifier_handler, NULL, vdev);
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
qemu_set_fd_handler(fd, NULL, NULL, vdev);
event_notifier_cleanup(&vdev->err_notifier);
@@ -2864,8 +2881,8 @@ static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev)
return;
}
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier),
@@ -2912,8 +2929,8 @@ static void vfio_register_req_notifier(VFIOPCIDevice *vdev)
fd = event_notifier_get_fd(&vdev->req_notifier);
qemu_set_fd_handler(fd, vfio_req_notifier_handler, NULL, vdev);
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
qemu_set_fd_handler(fd, NULL, NULL, vdev);
event_notifier_cleanup(&vdev->req_notifier);
@@ -2930,8 +2947,8 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev)
return;
}
- if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
+ if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name);
}
qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier),
@@ -2946,12 +2963,12 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
ERRP_GUARD();
VFIOPCIDevice *vdev = VFIO_PCI(pdev);
VFIODevice *vbasedev = &vdev->vbasedev;
- char *tmp, *subsys;
- Error *err = NULL;
+ char *subsys;
int i, ret;
bool is_mdev;
char uuid[UUID_STR_LEN];
- char *name;
+ g_autofree char *name = NULL;
+ g_autofree char *tmp = NULL;
if (vbasedev->fd < 0 && !vbasedev->sysfsdev) {
if (!(~vdev->host.domain || ~vdev->host.bus ||
@@ -2970,7 +2987,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
vdev->host.slot, vdev->host.function);
}
- if (vfio_device_get_name(vbasedev, errp) < 0) {
+ if (!vfio_device_get_name(vbasedev, errp)) {
return;
}
@@ -2982,7 +2999,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
*/
tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev);
subsys = realpath(tmp, NULL);
- g_free(tmp);
is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0);
free(subsys);
@@ -3001,16 +3017,12 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
name = g_strdup(vbasedev->name);
}
- ret = vfio_attach_device(name, vbasedev,
- pci_device_iommu_address_space(pdev), errp);
- g_free(name);
- if (ret) {
+ if (!vfio_attach_device(name, vbasedev,
+ pci_device_iommu_address_space(pdev), errp)) {
goto error;
}
- vfio_populate_device(vdev, &err);
- if (err) {
- error_propagate(errp, err);
+ if (!vfio_populate_device(vdev, errp)) {
goto error;
}
@@ -3103,16 +3115,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
vfio_bars_prepare(vdev);
- vfio_msix_early_setup(vdev, &err);
- if (err) {
- error_propagate(errp, err);
+ if (!vfio_msix_early_setup(vdev, errp)) {
goto error;
}
vfio_bars_register(vdev);
- ret = vfio_add_capabilities(vdev, errp);
- if (ret) {
+ if (!vfio_add_capabilities(vdev, errp)) {
goto out_teardown;
}
@@ -3126,7 +3135,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
if (!vdev->igd_opregion &&
vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) {
- struct vfio_region_info *opregion;
+ g_autofree struct vfio_region_info *opregion = NULL;
if (vdev->pdev.qdev.hotplugged) {
error_setg(errp,
@@ -3144,9 +3153,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
goto out_teardown;
}
- ret = vfio_pci_igd_opregion_init(vdev, opregion, errp);
- g_free(opregion);
- if (ret) {
+ if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) {
goto out_teardown;
}
}
@@ -3169,15 +3176,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
vfio_intx_routing_notifier);
vdev->irqchip_change_notifier.notify = vfio_irqchip_change;
kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier);
- ret = vfio_intx_enable(vdev, errp);
- if (ret) {
+ if (!vfio_intx_enable(vdev, errp)) {
goto out_deregister;
}
}
if (vdev->display != ON_OFF_AUTO_OFF) {
- ret = vfio_display_probe(vdev, errp);
- if (ret) {
+ if (!vfio_display_probe(vdev, errp)) {
goto out_deregister;
}
}
@@ -3362,6 +3367,8 @@ static Property vfio_pci_dev_properties[] = {
VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false),
DEFINE_PROP_ON_OFF_AUTO("enable-migration", VFIOPCIDevice,
vbasedev.enable_migration, ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice,
+ vbasedev.migration_events, false),
DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false),
DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice,
vbasedev.ram_block_discard_allowed, false),
@@ -3390,6 +3397,7 @@ static Property vfio_pci_dev_properties[] = {
DEFINE_PROP_LINK("iommufd", VFIOPCIDevice, vbasedev.iommufd,
TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *),
#endif
+ DEFINE_PROP_BOOL("skip-vsc-check", VFIOPCIDevice, skip_vsc_check, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index 6e64a2654e..bf67df2fbc 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -177,6 +177,7 @@ struct VFIOPCIDevice {
OnOffAuto ramfb_migrate;
bool defer_kvm_irq_routing;
bool clear_parent_atomics_on_exit;
+ bool skip_vsc_check;
VFIODisplay *dpy;
Notifier irqchip_change_notifier;
};
@@ -211,7 +212,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr);
void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr);
void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr);
void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
-int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp);
+bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp);
void vfio_quirk_reset(VFIOPCIDevice *vdev);
VFIOQuirk *vfio_quirk_alloc(int nr_mem);
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr);
@@ -224,14 +225,14 @@ bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name);
int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev,
struct vfio_pci_hot_reset_info **info_p);
-int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
+bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
-int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
- struct vfio_region_info *info,
- Error **errp);
+bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
+ struct vfio_region_info *info,
+ Error **errp);
void vfio_display_reset(VFIOPCIDevice *vdev);
-int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
+bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
void vfio_display_finalize(VFIOPCIDevice *vdev);
extern const VMStateDescription vfio_display_vmstate;
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
index dcd2365fb3..a85c199c76 100644
--- a/hw/vfio/platform.c
+++ b/hw/vfio/platform.c
@@ -115,18 +115,17 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp,
VFIODevice *vbasedev = &intp->vdev->vbasedev;
int32_t fd = event_notifier_get_fd(intp->interrupt);
Error *err = NULL;
- int ret;
qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp);
- ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0,
- VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err);
- if (ret) {
+ if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0,
+ VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name);
qemu_set_fd_handler(fd, NULL, NULL, NULL);
+ return -EINVAL;
}
- return ret;
+ return 0;
}
/*
@@ -355,15 +354,14 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp)
int32_t fd = event_notifier_get_fd(intp->unmask);
VFIODevice *vbasedev = &intp->vdev->vbasedev;
Error *err = NULL;
- int ret;
qemu_set_fd_handler(fd, NULL, NULL, NULL);
- ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0,
- VFIO_IRQ_SET_ACTION_UNMASK, fd, &err);
- if (ret) {
+ if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0,
+ VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) {
error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name);
+ return -EINVAL;
}
- return ret;
+ return 0;
}
/**
@@ -443,7 +441,7 @@ static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev)
* @errp: error object
*
*/
-static int vfio_populate_device(VFIODevice *vbasedev, Error **errp)
+static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp)
{
VFIOINTp *intp, *tmp;
int i, ret = -1;
@@ -452,7 +450,7 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp)
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
error_setg(errp, "this isn't a platform device");
- return ret;
+ return false;
}
vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions);
@@ -489,12 +487,11 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp)
irq.flags);
intp = vfio_init_intp(vbasedev, irq, errp);
if (!intp) {
- ret = -1;
goto irq_err;
}
}
}
- return 0;
+ return true;
irq_err:
timer_del(vdev->mmap_timer);
QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) {
@@ -509,7 +506,7 @@ reg_error:
g_free(vdev->regions[i]);
}
g_free(vdev->regions);
- return ret;
+ return false;
}
/* specialized functions for VFIO Platform devices */
@@ -529,10 +526,8 @@ static VFIODeviceOps vfio_platform_ops = {
* fd retrieval, resource query.
* Precondition: the device name must be initialized
*/
-static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
+static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
{
- int ret;
-
/* @fd takes precedence over @sysfsdev which takes precedence over @host */
if (vbasedev->fd < 0 && vbasedev->sysfsdev) {
g_free(vbasedev->name);
@@ -540,30 +535,28 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
} else if (vbasedev->fd < 0) {
if (!vbasedev->name || strchr(vbasedev->name, '/')) {
error_setg(errp, "wrong host device name");
- return -EINVAL;
+ return false;
}
vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s",
vbasedev->name);
}
- ret = vfio_device_get_name(vbasedev, errp);
- if (ret) {
- return ret;
+ if (!vfio_device_get_name(vbasedev, errp)) {
+ return false;
}
- ret = vfio_attach_device(vbasedev->name, vbasedev,
- &address_space_memory, errp);
- if (ret) {
- return ret;
+ if (!vfio_attach_device(vbasedev->name, vbasedev,
+ &address_space_memory, errp)) {
+ return false;
}
- ret = vfio_populate_device(vbasedev, errp);
- if (ret) {
- vfio_detach_device(vbasedev);
+ if (vfio_populate_device(vbasedev, errp)) {
+ return true;
}
- return ret;
+ vfio_detach_device(vbasedev);
+ return false;
}
/**
@@ -580,7 +573,7 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
VFIODevice *vbasedev = &vdev->vbasedev;
- int i, ret;
+ int i;
qemu_mutex_init(&vdev->intp_mutex);
@@ -588,9 +581,8 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
vbasedev->sysfsdev : vbasedev->name,
vdev->compat);
- ret = vfio_base_device_init(vbasedev, errp);
- if (ret) {
- goto out;
+ if (!vfio_base_device_init(vbasedev, errp)) {
+ goto init_err;
}
if (!vdev->compat) {
@@ -622,11 +614,9 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
}
sysbus_init_mmio(sbdev, vdev->regions[i]->mem);
}
-out:
- if (!ret) {
- return;
- }
+ return;
+init_err:
if (vdev->vbasedev.name) {
error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name);
} else {
diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c
index 0d949bb728..47b040f1bc 100644
--- a/hw/vfio/spapr.c
+++ b/hw/vfio/spapr.c
@@ -323,7 +323,7 @@ static int vfio_spapr_create_window(VFIOContainer *container,
return 0;
}
-static int
+static bool
vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer,
MemoryRegionSection *section,
Error **errp)
@@ -351,13 +351,13 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer,
error_setg(errp, "Container %p can't map guest IOVA region"
" 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx, container,
iova, end);
- return -EINVAL;
+ return false;
}
- return 0;
+ return true;
}
if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) {
- return 0;
+ return true;
}
/* For now intersections are not allowed, we may relax this later */
@@ -373,14 +373,14 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer,
section->offset_within_address_space +
int128_get64(section->size) - 1,
hostwin->min_iova, hostwin->max_iova);
- return -EINVAL;
+ return false;
}
}
ret = vfio_spapr_create_window(container, section, &pgsize);
if (ret) {
error_setg_errno(errp, -ret, "Failed to create SPAPR window");
- return ret;
+ return false;
}
vfio_host_win_add(scontainer, section->offset_within_address_space,
@@ -406,14 +406,14 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer,
"vfio: failed GROUP_SET_SPAPR_TCE for "
"KVM VFIO device %d and group fd %d",
param.tablefd, param.groupfd);
- return -errno;
+ return false;
}
trace_vfio_spapr_group_attach(param.groupfd, param.tablefd);
}
}
}
#endif
- return 0;
+ return true;
}
static void
@@ -458,8 +458,8 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer)
}
}
-static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
- Error **errp)
+static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
+ Error **errp)
{
VFIOContainer *container = container_of(bcontainer, VFIOContainer,
bcontainer);
@@ -480,7 +480,7 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
ret = ioctl(fd, VFIO_IOMMU_ENABLE);
if (ret) {
error_setg_errno(errp, errno, "failed to enable container");
- return -errno;
+ return false;
}
} else {
scontainer->prereg_listener = vfio_prereg_listener;
@@ -488,7 +488,6 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
memory_listener_register(&scontainer->prereg_listener,
&address_space_memory);
if (bcontainer->error) {
- ret = -1;
error_propagate_prepend(errp, bcontainer->error,
"RAM memory listener initialization failed: ");
goto listener_unregister_exit;
@@ -500,7 +499,6 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
if (ret) {
error_setg_errno(errp, errno,
"VFIO_IOMMU_SPAPR_TCE_GET_INFO failed");
- ret = -errno;
goto listener_unregister_exit;
}
@@ -527,13 +525,13 @@ static int vfio_spapr_container_setup(VFIOContainerBase *bcontainer,
0x1000);
}
- return 0;
+ return true;
listener_unregister_exit:
if (v2) {
memory_listener_unregister(&scontainer->prereg_listener);
}
- return ret;
+ return false;
}
static void vfio_iommu_spapr_class_init(ObjectClass *klass, void *data)
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index f0474b244b..64161bf6f4 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -152,7 +152,8 @@ vfio_load_device_config_state(const char *name) " (%s)"
vfio_load_state(const char *name, uint64_t data) " (%s) data 0x%"PRIx64
vfio_load_state_device_data(const char *name, uint64_t data_size, int ret) " (%s) size 0x%"PRIx64" ret %d"
vfio_migration_realize(const char *name) " (%s)"
-vfio_migration_set_state(const char *name, const char *state) " (%s) state %s"
+vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s"
+vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover state %s"
vfio_migration_state_notifier(const char *name, int state) " (%s) state %d"
vfio_save_block(const char *name, int data_size) " (%s) data_size %d"
vfio_save_cleanup(const char *name) " (%s)"
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index e827b9175f..ed99ab8745 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -208,6 +208,7 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
void *vaddr;
int ret;
Int128 llend;
+ Error *local_err = NULL;
if (iotlb->target_as != &address_space_memory) {
error_report("Wrong target AS \"%s\", only system memory is allowed",
@@ -227,7 +228,9 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
bool read_only;
- if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL)) {
+ if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL,
+ &local_err)) {
+ error_report_err(local_err);
return;
}
ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova,
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
index fb82cc33e4..95b207ac8b 100644
--- a/hw/xen/xen-bus.c
+++ b/hw/xen/xen-bus.c
@@ -13,6 +13,7 @@
#include "hw/sysbus.h"
#include "hw/xen/xen.h"
#include "hw/xen/xen-backend.h"
+#include "hw/xen/xen-legacy-backend.h" /* xen_be_init() */
#include "hw/xen/xen-bus.h"
#include "hw/xen/xen-bus-helper.h"
#include "monitor/monitor.h"
@@ -329,6 +330,9 @@ static void xen_bus_realize(BusState *bus, Error **errp)
goto fail;
}
+ /* Initialize legacy backend core & drivers */
+ xen_be_init();
+
if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */
"domid", NULL, "%u", &domid) == 1) {
xenbus->backend_id = domid;
diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c
index 1627da7398..2d1b032121 100644
--- a/hw/xen/xen-hvm-common.c
+++ b/hw/xen/xen-hvm-common.c
@@ -872,8 +872,6 @@ void xen_register_ioreq(XenIOState *state, unsigned int max_cpus,
xen_bus_init();
- xen_be_init();
-
return;
err:
diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c
index 124dd5f3d6..6f0b300a42 100644
--- a/hw/xen/xen-legacy-backend.c
+++ b/hw/xen/xen-legacy-backend.c
@@ -622,27 +622,11 @@ void xen_be_init(void)
qbus_set_bus_hotplug_handler(xen_sysbus);
xen_set_dynamic_sysbus();
-
- xen_be_register("vkbd", &xen_kbdmouse_ops);
-#ifdef CONFIG_VIRTFS
- xen_be_register("9pfs", &xen_9pfs_ops);
-#endif
-#ifdef CONFIG_USB_LIBUSB
- xen_be_register("qusb", &xen_usb_ops);
-#endif
}
int xen_be_register(const char *type, struct XenDevOps *ops)
{
char path[50];
- int rc;
-
- if (ops->backend_register) {
- rc = ops->backend_register();
- if (rc) {
- return rc;
- }
- }
snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid,
type);
diff --git a/hw/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c
index 7f59080ba7..fa6813b1ad 100644
--- a/hw/xen/xen-mapcache.c
+++ b/hw/xen/xen-mapcache.c
@@ -74,14 +74,14 @@ typedef struct MapCache {
static MapCache *mapcache;
-static inline void mapcache_lock(void)
+static inline void mapcache_lock(MapCache *mc)
{
- qemu_mutex_lock(&mapcache->lock);
+ qemu_mutex_lock(&mc->lock);
}
-static inline void mapcache_unlock(void)
+static inline void mapcache_unlock(MapCache *mc)
{
- qemu_mutex_unlock(&mapcache->lock);
+ qemu_mutex_unlock(&mc->lock);
}
static inline int test_bits(int nr, int size, const unsigned long *addr)
@@ -93,23 +93,44 @@ static inline int test_bits(int nr, int size, const unsigned long *addr)
return 0;
}
-void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
+static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f,
+ void *opaque,
+ unsigned long max_size)
{
unsigned long size;
- struct rlimit rlimit_as;
+ MapCache *mc;
+
+ mc = g_new0(MapCache, 1);
+
+ mc->phys_offset_to_gaddr = f;
+ mc->opaque = opaque;
+ qemu_mutex_init(&mc->lock);
+
+ QTAILQ_INIT(&mc->locked_entries);
- mapcache = g_new0(MapCache, 1);
+ mc->max_mcache_size = max_size;
- mapcache->phys_offset_to_gaddr = f;
- mapcache->opaque = opaque;
- qemu_mutex_init(&mapcache->lock);
+ mc->nr_buckets =
+ (((mc->max_mcache_size >> XC_PAGE_SHIFT) +
+ (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >>
+ (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT));
- QTAILQ_INIT(&mapcache->locked_entries);
+ size = mc->nr_buckets * sizeof(MapCacheEntry);
+ size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1);
+ trace_xen_map_cache_init(mc->nr_buckets, size);
+ mc->entry = g_malloc0(size);
+ return mc;
+}
+
+void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
+{
+ struct rlimit rlimit_as;
+ unsigned long max_mcache_size;
if (geteuid() == 0) {
rlimit_as.rlim_cur = RLIM_INFINITY;
rlimit_as.rlim_max = RLIM_INFINITY;
- mapcache->max_mcache_size = MCACHE_MAX_SIZE;
+ max_mcache_size = MCACHE_MAX_SIZE;
} else {
getrlimit(RLIMIT_AS, &rlimit_as);
rlimit_as.rlim_cur = rlimit_as.rlim_max;
@@ -119,27 +140,18 @@ void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque)
" memory is not infinity");
}
if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) {
- mapcache->max_mcache_size = rlimit_as.rlim_max -
- NON_MCACHE_MEMORY_SIZE;
+ max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE;
} else {
- mapcache->max_mcache_size = MCACHE_MAX_SIZE;
+ max_mcache_size = MCACHE_MAX_SIZE;
}
}
+ mapcache = xen_map_cache_init_single(f, opaque, max_mcache_size);
setrlimit(RLIMIT_AS, &rlimit_as);
-
- mapcache->nr_buckets =
- (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) +
- (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >>
- (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT));
-
- size = mapcache->nr_buckets * sizeof (MapCacheEntry);
- size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1);
- trace_xen_map_cache_init(mapcache->nr_buckets, size);
- mapcache->entry = g_malloc0(size);
}
-static void xen_remap_bucket(MapCacheEntry *entry,
+static void xen_remap_bucket(MapCache *mc,
+ MapCacheEntry *entry,
void *vaddr,
hwaddr size,
hwaddr address_index,
@@ -240,8 +252,9 @@ static void xen_remap_bucket(MapCacheEntry *entry,
g_free(err);
}
-static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size,
- uint8_t lock, bool dma)
+static uint8_t *xen_map_cache_unlocked(MapCache *mc,
+ hwaddr phys_addr, hwaddr size,
+ uint8_t lock, bool dma, bool is_write)
{
MapCacheEntry *entry, *pentry = NULL,
*free_entry = NULL, *free_pentry = NULL;
@@ -269,16 +282,16 @@ tryagain:
test_bit_size = XC_PAGE_SIZE;
}
- if (mapcache->last_entry != NULL &&
- mapcache->last_entry->paddr_index == address_index &&
+ if (mc->last_entry != NULL &&
+ mc->last_entry->paddr_index == address_index &&
!lock && !size &&
test_bits(address_offset >> XC_PAGE_SHIFT,
test_bit_size >> XC_PAGE_SHIFT,
- mapcache->last_entry->valid_mapping)) {
+ mc->last_entry->valid_mapping)) {
trace_xen_map_cache_return(
- mapcache->last_entry->vaddr_base + address_offset
+ mc->last_entry->vaddr_base + address_offset
);
- return mapcache->last_entry->vaddr_base + address_offset;
+ return mc->last_entry->vaddr_base + address_offset;
}
/* size is always a multiple of MCACHE_BUCKET_SIZE */
@@ -291,7 +304,7 @@ tryagain:
cache_size = MCACHE_BUCKET_SIZE;
}
- entry = &mapcache->entry[address_index % mapcache->nr_buckets];
+ entry = &mc->entry[address_index % mc->nr_buckets];
while (entry && (lock || entry->lock) && entry->vaddr_base &&
(entry->paddr_index != address_index || entry->size != cache_size ||
@@ -312,24 +325,24 @@ tryagain:
if (!entry) {
entry = g_new0(MapCacheEntry, 1);
pentry->next = entry;
- xen_remap_bucket(entry, NULL, cache_size, address_index, dummy);
+ xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy);
} else if (!entry->lock) {
if (!entry->vaddr_base || entry->paddr_index != address_index ||
entry->size != cache_size ||
!test_bits(address_offset >> XC_PAGE_SHIFT,
test_bit_size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
- xen_remap_bucket(entry, NULL, cache_size, address_index, dummy);
+ xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy);
}
}
if(!test_bits(address_offset >> XC_PAGE_SHIFT,
test_bit_size >> XC_PAGE_SHIFT,
entry->valid_mapping)) {
- mapcache->last_entry = NULL;
+ mc->last_entry = NULL;
#ifdef XEN_COMPAT_PHYSMAP
- if (!translated && mapcache->phys_offset_to_gaddr) {
- phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size);
+ if (!translated && mc->phys_offset_to_gaddr) {
+ phys_addr = mc->phys_offset_to_gaddr(phys_addr, size);
translated = true;
goto tryagain;
}
@@ -342,7 +355,7 @@ tryagain:
return NULL;
}
- mapcache->last_entry = entry;
+ mc->last_entry = entry;
if (lock) {
MapCacheRev *reventry = g_new0(MapCacheRev, 1);
entry->lock++;
@@ -352,30 +365,32 @@ tryagain:
abort();
}
reventry->dma = dma;
- reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset;
- reventry->paddr_index = mapcache->last_entry->paddr_index;
+ reventry->vaddr_req = mc->last_entry->vaddr_base + address_offset;
+ reventry->paddr_index = mc->last_entry->paddr_index;
reventry->size = entry->size;
- QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next);
+ QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next);
}
trace_xen_map_cache_return(
- mapcache->last_entry->vaddr_base + address_offset
+ mc->last_entry->vaddr_base + address_offset
);
- return mapcache->last_entry->vaddr_base + address_offset;
+ return mc->last_entry->vaddr_base + address_offset;
}
-uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size,
- uint8_t lock, bool dma)
+uint8_t *xen_map_cache(MemoryRegion *mr,
+ hwaddr phys_addr, hwaddr size,
+ uint8_t lock, bool dma,
+ bool is_write)
{
uint8_t *p;
- mapcache_lock();
- p = xen_map_cache_unlocked(phys_addr, size, lock, dma);
- mapcache_unlock();
+ mapcache_lock(mapcache);
+ p = xen_map_cache_unlocked(mapcache, phys_addr, size, lock, dma, is_write);
+ mapcache_unlock(mapcache);
return p;
}
-ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
+static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr)
{
MapCacheEntry *entry = NULL;
MapCacheRev *reventry;
@@ -384,8 +399,8 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
ram_addr_t raddr;
int found = 0;
- mapcache_lock();
- QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
+ mapcache_lock(mc);
+ QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
if (reventry->vaddr_req == ptr) {
paddr_index = reventry->paddr_index;
size = reventry->size;
@@ -395,30 +410,32 @@ ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
}
if (!found) {
trace_xen_ram_addr_from_mapcache_not_found(ptr);
- QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
- trace_xen_ram_addr_from_mapcache_found(reventry->paddr_index,
- reventry->vaddr_req);
- }
- abort();
- return 0;
+ mapcache_unlock(mc);
+ return RAM_ADDR_INVALID;
}
- entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
+ entry = &mc->entry[paddr_index % mc->nr_buckets];
while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
entry = entry->next;
}
if (!entry) {
trace_xen_ram_addr_from_mapcache_not_in_cache(ptr);
- raddr = 0;
+ raddr = RAM_ADDR_INVALID;
} else {
raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) +
((unsigned long) ptr - (unsigned long) entry->vaddr_base);
}
- mapcache_unlock();
+ mapcache_unlock(mc);
return raddr;
}
-static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer)
+ram_addr_t xen_ram_addr_from_mapcache(void *ptr)
+{
+ return xen_ram_addr_from_mapcache_single(mapcache, ptr);
+}
+
+static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc,
+ uint8_t *buffer)
{
MapCacheEntry *entry = NULL, *pentry = NULL;
MapCacheRev *reventry;
@@ -426,7 +443,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer)
hwaddr size;
int found = 0;
- QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
+ QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
if (reventry->vaddr_req == buffer) {
paddr_index = reventry->paddr_index;
size = reventry->size;
@@ -436,7 +453,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer)
}
if (!found) {
trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer);
- QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
+ QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
trace_xen_invalidate_map_cache_entry_unlocked_found(
reventry->paddr_index,
reventry->vaddr_req
@@ -444,15 +461,15 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer)
}
return;
}
- QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next);
+ QTAILQ_REMOVE(&mc->locked_entries, reventry, next);
g_free(reventry);
- if (mapcache->last_entry != NULL &&
- mapcache->last_entry->paddr_index == paddr_index) {
- mapcache->last_entry = NULL;
+ if (mc->last_entry != NULL &&
+ mc->last_entry->paddr_index == paddr_index) {
+ mc->last_entry = NULL;
}
- entry = &mapcache->entry[paddr_index % mapcache->nr_buckets];
+ entry = &mc->entry[paddr_index % mc->nr_buckets];
while (entry && (entry->paddr_index != paddr_index || entry->size != size)) {
pentry = entry;
entry = entry->next;
@@ -485,9 +502,9 @@ static void xen_invalidate_map_cache_entry_bh(void *opaque)
{
XenMapCacheData *data = opaque;
- mapcache_lock();
- xen_invalidate_map_cache_entry_unlocked(data->buffer);
- mapcache_unlock();
+ mapcache_lock(mapcache);
+ xen_invalidate_map_cache_entry_unlocked(mapcache, data->buffer);
+ mapcache_unlock(mapcache);
aio_co_wake(data->co);
}
@@ -503,23 +520,20 @@ void coroutine_mixed_fn xen_invalidate_map_cache_entry(uint8_t *buffer)
xen_invalidate_map_cache_entry_bh, &data);
qemu_coroutine_yield();
} else {
- mapcache_lock();
- xen_invalidate_map_cache_entry_unlocked(buffer);
- mapcache_unlock();
+ mapcache_lock(mapcache);
+ xen_invalidate_map_cache_entry_unlocked(mapcache, buffer);
+ mapcache_unlock(mapcache);
}
}
-void xen_invalidate_map_cache(void)
+static void xen_invalidate_map_cache_single(MapCache *mc)
{
unsigned long i;
MapCacheRev *reventry;
- /* Flush pending AIO before destroying the mapcache */
- bdrv_drain_all();
-
- mapcache_lock();
+ mapcache_lock(mc);
- QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) {
+ QTAILQ_FOREACH(reventry, &mc->locked_entries, next) {
if (!reventry->dma) {
continue;
}
@@ -527,8 +541,8 @@ void xen_invalidate_map_cache(void)
reventry->vaddr_req);
}
- for (i = 0; i < mapcache->nr_buckets; i++) {
- MapCacheEntry *entry = &mapcache->entry[i];
+ for (i = 0; i < mc->nr_buckets; i++) {
+ MapCacheEntry *entry = &mc->entry[i];
if (entry->vaddr_base == NULL) {
continue;
@@ -549,12 +563,21 @@ void xen_invalidate_map_cache(void)
entry->valid_mapping = NULL;
}
- mapcache->last_entry = NULL;
+ mc->last_entry = NULL;
+
+ mapcache_unlock(mc);
+}
+
+void xen_invalidate_map_cache(void)
+{
+ /* Flush pending AIO before destroying the mapcache */
+ bdrv_drain_all();
- mapcache_unlock();
+ xen_invalidate_map_cache_single(mapcache);
}
-static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr,
+static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc,
+ hwaddr old_phys_addr,
hwaddr new_phys_addr,
hwaddr size)
{
@@ -576,7 +599,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr,
cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE);
}
- entry = &mapcache->entry[address_index % mapcache->nr_buckets];
+ entry = &mc->entry[address_index % mc->nr_buckets];
while (entry && !(entry->paddr_index == address_index &&
entry->size == cache_size)) {
entry = entry->next;
@@ -591,7 +614,7 @@ static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr,
trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr);
- xen_remap_bucket(entry, entry->vaddr_base,
+ xen_remap_bucket(mc, entry, entry->vaddr_base,
cache_size, address_index, false);
if (!test_bits(address_offset >> XC_PAGE_SHIFT,
test_bit_size >> XC_PAGE_SHIFT,
@@ -611,8 +634,9 @@ uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr,
{
uint8_t *p;
- mapcache_lock();
- p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size);
- mapcache_unlock();
+ mapcache_lock(mapcache);
+ p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr,
+ new_phys_addr, size);
+ mapcache_unlock(mapcache);
return p;
}
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index 1130d1a147..b500ce0989 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -34,8 +34,7 @@ static void xen_init_pv(MachineState *machine)
{
setup_xen_backend_ops();
- /* Initialize backend core & drivers */
- xen_be_init();
+ xen_bus_init();
switch (xen_mode) {
case XEN_ATTACH:
@@ -60,8 +59,6 @@ static void xen_init_pv(MachineState *machine)
vga_interface_created = true;
}
- xen_bus_init();
-
/* config cleanup hook */
atexit(xen_config_cleanup);
}
diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig
index 0740657ea5..fc5c785cfa 100644
--- a/hw/xtensa/Kconfig
+++ b/hw/xtensa/Kconfig
@@ -1,14 +1,21 @@
config XTENSA_SIM
+ default y
+ depends on XTENSA
bool
config XTENSA_VIRT
bool
+ default y
+ depends on XTENSA
select XTENSA_SIM
select PCI_EXPRESS_GENERIC_BRIDGE
select PCI_DEVICES
config XTENSA_XTFPGA
bool
+ default y
+ depends on XTENSA && FDT
+ select DEVICE_TREE
select OPENCORES_ETH
select PFLASH_CFI01
select SERIAL
diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index f49e6591dc..955e8867a3 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -356,7 +356,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine)
cur_tagptr = put_tag(cur_tagptr, BP_TAG_COMMAND_LINE,
strlen(kernel_cmdline) + 1, kernel_cmdline);
}
-#ifdef CONFIG_FDT
if (dtb_filename) {
int fdt_size;
void *fdt = load_device_tree(dtb_filename, &fdt_size);
@@ -373,14 +372,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine)
cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB);
g_free(fdt);
}
-#else
- if (dtb_filename) {
- error_report("could not load DTB '%s': "
- "FDT support is not configured in QEMU",
- dtb_filename);
- exit(EXIT_FAILURE);
- }
-#endif
if (initrd_filename) {
BpMemInfo initrd_location = { 0 };
int initrd_size = load_ramdisk(initrd_filename, cur_lowmem,
diff --git a/include/disas/disas.h b/include/disas/disas.h
index 176775eff7..c702b1effc 100644
--- a/include/disas/disas.h
+++ b/include/disas/disas.h
@@ -2,13 +2,18 @@
#define QEMU_DISAS_H
/* Disassemble this for me please... (debugging). */
+#ifdef CONFIG_TCG
void disas(FILE *out, const void *code, size_t size);
-void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size);
+void target_disas(FILE *out, CPUState *cpu, const DisasContextBase *db);
+#endif
void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc,
int nb_insn, bool is_physical);
-char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size);
+#ifdef CONFIG_PLUGIN
+char *plugin_disas(CPUState *cpu, const DisasContextBase *db,
+ uint64_t addr, size_t size);
+#endif
/* Look up symbol for debugging purpose. Returns "" if unknown. */
const char *lookup_symbol(uint64_t orig_addr);
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index e75ec13cd0..6f09b86e7f 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -19,6 +19,7 @@
#ifndef CPU_ALL_H
#define CPU_ALL_H
+#include "exec/page-protection.h"
#include "exec/cpu-common.h"
#include "exec/memory.h"
#include "exec/tswap.h"
@@ -64,7 +65,7 @@
/* MMU memory access macros */
#if defined(CONFIG_USER_ONLY)
-#include "exec/user/abitypes.h"
+#include "user/abitypes.h"
/*
* If non-zero, the guest virtual address space is a contiguous subset
@@ -139,33 +140,24 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val
#ifdef TARGET_PAGE_BITS_VARY
# include "exec/page-vary.h"
extern const TargetPageBits target_page;
-#ifdef CONFIG_DEBUG_TCG
-#define TARGET_PAGE_BITS ({ assert(target_page.decided); target_page.bits; })
-#define TARGET_PAGE_MASK ({ assert(target_page.decided); \
- (target_long)target_page.mask; })
+# ifdef CONFIG_DEBUG_TCG
+# define TARGET_PAGE_BITS ({ assert(target_page.decided); \
+ target_page.bits; })
+# define TARGET_PAGE_MASK ({ assert(target_page.decided); \
+ (target_long)target_page.mask; })
+# else
+# define TARGET_PAGE_BITS target_page.bits
+# define TARGET_PAGE_MASK ((target_long)target_page.mask)
+# endif
+# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK)
#else
-#define TARGET_PAGE_BITS target_page.bits
-#define TARGET_PAGE_MASK ((target_long)target_page.mask)
-#endif
-#define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK)
-#else
-#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS
-#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
-#define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS)
+# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS
+# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
+# define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS)
#endif
#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE)
-#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
-/* FIXME: Code that sets/uses this is broken and needs to go away. */
-#define PAGE_RESERVED 0x0100
-#endif
-/*
- * For linux-user, indicates that the page is mapped with the same semantics
- * in both guest and host.
- */
-#define PAGE_PASSTHROUGH 0x0800
-
#if defined(CONFIG_USER_ONLY)
void page_dump(FILE *f);
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 6d5318895a..815342d043 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -1,14 +1,20 @@
+/*
+ * CPU interfaces that are target independent.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
#ifndef CPU_COMMON_H
#define CPU_COMMON_H
-/* CPU interfaces that are target independent. */
-
#include "exec/vaddr.h"
#ifndef CONFIG_USER_ONLY
#include "exec/hwaddr.h"
#endif
#include "hw/core/cpu.h"
#include "tcg/debug-assert.h"
+#include "exec/page-protection.h"
#define EXCP_INTERRUPT 0x10000 /* async interruption */
#define EXCP_HLT 0x10001 /* hlt instruction reached */
@@ -141,8 +147,6 @@ void *cpu_physical_memory_map(hwaddr addr,
bool is_write);
void cpu_physical_memory_unmap(void *buffer, hwaddr len,
bool is_write, hwaddr access_len);
-void cpu_register_map_client(QEMUBH *bh);
-void cpu_unregister_map_client(QEMUBH *bh);
bool cpu_physical_memory_is_io(hwaddr phys_addr);
@@ -172,6 +176,13 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
void list_cpus(void);
#ifdef CONFIG_TCG
+
+bool tcg_cflags_has(CPUState *cpu, uint32_t flags);
+void tcg_cflags_set(CPUState *cpu, uint32_t flags);
+
+/* current cflags for hashing/comparison */
+uint32_t curr_cflags(CPUState *cpu);
+
/**
* cpu_unwind_state_data:
* @cpu: the cpu context
@@ -203,36 +214,6 @@ G_NORETURN void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc);
G_NORETURN void cpu_loop_exit(CPUState *cpu);
G_NORETURN void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc);
-/* same as PROT_xxx */
-#define PAGE_READ 0x0001
-#define PAGE_WRITE 0x0002
-#define PAGE_EXEC 0x0004
-#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
-#define PAGE_VALID 0x0008
-/*
- * Original state of the write flag (used when tracking self-modifying code)
- */
-#define PAGE_WRITE_ORG 0x0010
-/*
- * Invalidate the TLB entry immediately, helpful for s390x
- * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs()
- */
-#define PAGE_WRITE_INV 0x0020
-/* For use with page_set_flags: page is being replaced; target_data cleared. */
-#define PAGE_RESET 0x0040
-/* For linux-user, indicates that the page is MAP_ANON. */
-#define PAGE_ANON 0x0080
-
-/* Target-specific bits that will be used via page_get_flags(). */
-#define PAGE_TARGET_1 0x0200
-#define PAGE_TARGET_2 0x0400
-
-/*
- * For linux-user, indicates that the page is mapped with the same semantics
- * in both guest and host.
- */
-#define PAGE_PASSTHROUGH 0x0800
-
/* accel/tcg/cpu-exec.c */
int cpu_exec(CPUState *cpu);
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index 11ba3778ba..71009f84f5 100644
--- a/include/exec/cpu_ldst.h
+++ b/include/exec/cpu_ldst.h
@@ -355,16 +355,6 @@ uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr);
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr);
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr);
-static inline int cpu_ldsb_code(CPUArchState *env, abi_ptr addr)
-{
- return (int8_t)cpu_ldub_code(env, addr);
-}
-
-static inline int cpu_ldsw_code(CPUArchState *env, abi_ptr addr)
-{
- return (int16_t)cpu_lduw_code(env, addr);
-}
-
/**
* tlb_vaddr_to_host:
* @env: CPUArchState
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 4c5e470581..2cd7b8f61b 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -510,9 +510,6 @@ static inline void tb_set_page_addr1(TranslationBlock *tb,
#endif
}
-/* current cflags for hashing/comparison */
-uint32_t curr_cflags(CPUState *cpu);
-
/* TranslationBlock invalidate API */
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last);
diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h
index 5d6d78a625..834590dc4e 100644
--- a/include/exec/helper-gen-common.h
+++ b/include/exec/helper-gen-common.h
@@ -11,8 +11,4 @@
#include "exec/helper-gen.h.inc"
#undef HELPER_H
-#define HELPER_H "accel/tcg/plugin-helpers.h"
-#include "exec/helper-gen.h.inc"
-#undef HELPER_H
-
#endif /* HELPER_GEN_COMMON_H */
diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc
index d9fd3ed72a..dabe138e20 100644
--- a/include/exec/helper-gen.h.inc
+++ b/include/exec/helper-gen.h.inc
@@ -14,7 +14,8 @@
extern TCGHelperInfo glue(helper_info_, name); \
static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \
{ \
- tcg_gen_call0(&glue(helper_info_, name), dh_retvar(ret)); \
+ tcg_gen_call0(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret)); \
}
#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \
@@ -22,7 +23,8 @@ extern TCGHelperInfo glue(helper_info_, name); \
static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1)) \
{ \
- tcg_gen_call1(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call1(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1)); \
}
@@ -31,7 +33,8 @@ extern TCGHelperInfo glue(helper_info_, name); \
static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \
{ \
- tcg_gen_call2(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call2(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2)); \
}
@@ -40,7 +43,8 @@ extern TCGHelperInfo glue(helper_info_, name); \
static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \
{ \
- tcg_gen_call3(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call3(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \
}
@@ -50,7 +54,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \
dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \
{ \
- tcg_gen_call4(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call4(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2), \
dh_arg(t3, 3), dh_arg(t4, 4)); \
}
@@ -61,7 +66,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \
dh_arg_decl(t4, 4), dh_arg_decl(t5, 5)) \
{ \
- tcg_gen_call5(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call5(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \
dh_arg(t4, 4), dh_arg(t5, 5)); \
}
@@ -72,7 +78,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), \
dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6)) \
{ \
- tcg_gen_call6(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call6(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \
dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6)); \
}
@@ -84,7 +91,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \
dh_arg_decl(t4, 4), dh_arg_decl(t5, 5), dh_arg_decl(t6, 6), \
dh_arg_decl(t7, 7)) \
{ \
- tcg_gen_call7(&glue(helper_info_, name), dh_retvar(ret), \
+ tcg_gen_call7(glue(helper_info_,name).func, \
+ &glue(helper_info_,name), dh_retvar(ret), \
dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3), \
dh_arg(t4, 4), dh_arg(t5, 5), dh_arg(t6, 6), \
dh_arg(t7, 7)); \
diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h
index 8b67170a22..16782ef46c 100644
--- a/include/exec/helper-proto-common.h
+++ b/include/exec/helper-proto-common.h
@@ -13,8 +13,4 @@
#include "exec/helper-proto.h.inc"
#undef HELPER_H
-#define HELPER_H "accel/tcg/plugin-helpers.h"
-#include "exec/helper-proto.h.inc"
-#undef HELPER_H
-
#endif /* HELPER_PROTO_COMMON_H */
diff --git a/include/exec/memory.h b/include/exec/memory.h
index dadb5cd65a..9cdd64e9c6 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -774,9 +774,22 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
RamDiscardListener *rdl);
+/**
+ * memory_get_xlat_addr: Extract addresses from a TLB entry
+ *
+ * @iotlb: pointer to an #IOMMUTLBEntry
+ * @vaddr: virtual address
+ * @ram_addr: RAM address
+ * @read_only: indicates if writes are allowed
+ * @mr_has_discard_manager: indicates memory is controlled by a
+ * RamDiscardManager
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Return: true on success, else false setting @errp with error.
+ */
bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only,
- bool *mr_has_discard_manager);
+ bool *mr_has_discard_manager, Error **errp);
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
@@ -1112,6 +1125,19 @@ struct MemoryListener {
QTAILQ_ENTRY(MemoryListener) link_as;
};
+typedef struct AddressSpaceMapClient {
+ QEMUBH *bh;
+ QLIST_ENTRY(AddressSpaceMapClient) link;
+} AddressSpaceMapClient;
+
+typedef struct {
+ MemoryRegion *mr;
+ void *buffer;
+ hwaddr addr;
+ hwaddr len;
+ bool in_use;
+} BounceBuffer;
+
/**
* struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects
*/
@@ -1129,6 +1155,12 @@ struct AddressSpace {
struct MemoryRegionIoeventfd *ioeventfds;
QTAILQ_HEAD(, MemoryListener) listeners;
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
+
+ /* Bounce buffer to use for this address space. */
+ BounceBuffer bounce;
+ /* List of callbacks to invoke when buffers free up */
+ QemuMutex map_client_list_lock;
+ QLIST_HEAD(, AddressSpaceMapClient) map_client_list;
};
typedef struct AddressSpaceDispatch AddressSpaceDispatch;
@@ -2946,8 +2978,8 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len,
* May return %NULL and set *@plen to zero(0), if resources needed to perform
* the mapping are exhausted.
* Use only for reads OR writes - not for read-modify-write operations.
- * Use cpu_register_map_client() to know when retrying the map operation is
- * likely to succeed.
+ * Use address_space_register_map_client() to know when retrying the map
+ * operation is likely to succeed.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
@@ -2972,6 +3004,28 @@ void *address_space_map(AddressSpace *as, hwaddr addr,
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
bool is_write, hwaddr access_len);
+/*
+ * address_space_register_map_client: Register a callback to invoke when
+ * resources for address_space_map() are available again.
+ *
+ * address_space_map may fail when there are not enough resources available,
+ * such as when bounce buffer memory would exceed the limit. The callback can
+ * be used to retry the address_space_map operation. Note that the callback
+ * gets automatically removed after firing.
+ *
+ * @as: #AddressSpace to be accessed
+ * @bh: callback to invoke when address_space_map() retry is appropriate
+ */
+void address_space_register_map_client(AddressSpace *as, QEMUBH *bh);
+
+/*
+ * address_space_unregister_map_client: Unregister a callback that has
+ * previously been registered and not fired yet.
+ *
+ * @as: #AddressSpace to be accessed
+ * @bh: callback to unregister
+ */
+void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh);
/* Internal functions, part of the implementation of address_space_read. */
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h
new file mode 100644
index 0000000000..c43231af8b
--- /dev/null
+++ b/include/exec/page-protection.h
@@ -0,0 +1,41 @@
+/*
+ * QEMU page protection definitions.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+#ifndef EXEC_PAGE_PROT_COMMON_H
+#define EXEC_PAGE_PROT_COMMON_H
+
+/* same as PROT_xxx */
+#define PAGE_READ 0x0001
+#define PAGE_WRITE 0x0002
+#define PAGE_EXEC 0x0004
+#define PAGE_RWX (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
+#define PAGE_VALID 0x0008
+/*
+ * Original state of the write flag (used when tracking self-modifying code)
+ */
+#define PAGE_WRITE_ORG 0x0010
+/*
+ * Invalidate the TLB entry immediately, helpful for s390x
+ * Low-Address-Protection. Used with PAGE_WRITE in tlb_set_page_with_attrs()
+ */
+#define PAGE_WRITE_INV 0x0020
+/* For use with page_set_flags: page is being replaced; target_data cleared. */
+#define PAGE_RESET 0x0040
+/* For linux-user, indicates that the page is MAP_ANON. */
+#define PAGE_ANON 0x0080
+
+/* Target-specific bits that will be used via page_get_flags(). */
+#define PAGE_TARGET_1 0x0200
+#define PAGE_TARGET_2 0x0400
+
+/*
+ * For linux-user, indicates that the page is mapped with the same semantics
+ * in both guest and host.
+ */
+#define PAGE_PASSTHROUGH 0x0800
+
+#endif
diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
index c4552b5061..cbb2ca2131 100644
--- a/include/exec/plugin-gen.h
+++ b/include/exec/plugin-gen.h
@@ -18,19 +18,17 @@ struct DisasContextBase;
#ifdef CONFIG_PLUGIN
-bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db,
- bool supress);
+bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db);
void plugin_gen_tb_end(CPUState *cpu, size_t num_insns);
void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db);
void plugin_gen_insn_end(void);
void plugin_gen_disable_mem_helpers(void);
-void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info);
#else /* !CONFIG_PLUGIN */
-static inline bool
-plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, bool sup)
+static inline
+bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db)
{
return false;
}
@@ -48,9 +46,6 @@ static inline void plugin_gen_tb_end(CPUState *cpu, size_t num_insns)
static inline void plugin_gen_disable_mem_helpers(void)
{ }
-static inline void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
-{ }
-
#endif /* CONFIG_PLUGIN */
#endif /* QEMU_PLUGIN_GEN_H */
diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h
index 48211c890a..a6d1af6e9b 100644
--- a/include/exec/translation-block.h
+++ b/include/exec/translation-block.h
@@ -77,6 +77,7 @@ struct TranslationBlock {
#define CF_PARALLEL 0x00008000 /* Generate code for a parallel context */
#define CF_NOIRQ 0x00010000 /* Generate an uninterruptible TB */
#define CF_PCREL 0x00020000 /* Opcodes in TB are PC-relative */
+#define CF_BP_PAGE 0x00040000 /* Breakpoint present in code page */
#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */
#define CF_CLUSTER_SHIFT 24
diff --git a/include/exec/translator.h b/include/exec/translator.h
index 6cd937ac5c..25004dfb76 100644
--- a/include/exec/translator.h
+++ b/include/exec/translator.h
@@ -19,10 +19,7 @@
*/
#include "qemu/bswap.h"
-#include "exec/cpu-common.h"
-#include "exec/cpu-defs.h"
-#include "exec/abi_ptr.h"
-#include "cpu.h"
+#include "exec/vaddr.h"
/**
* gen_intermediate_code
@@ -75,14 +72,14 @@ typedef enum DisasJumpType {
* @num_insns: Number of translated instructions (including current).
* @max_insns: Maximum number of instructions to be translated in this TB.
* @singlestep_enabled: "Hardware" single stepping enabled.
- * @saved_can_do_io: Known value of cpu->neg.can_do_io, or -1 for unknown.
* @plugin_enabled: TCG plugin enabled in this TB.
+ * @fake_insn: True if translator_fake_ldb used.
* @insn_start: The last op emitted by the insn_start hook,
* which is expected to be INDEX_op_insn_start.
*
* Architecture-agnostic disassembly context.
*/
-typedef struct DisasContextBase {
+struct DisasContextBase {
TranslationBlock *tb;
vaddr pc_first;
vaddr pc_next;
@@ -91,9 +88,22 @@ typedef struct DisasContextBase {
int max_insns;
bool singlestep_enabled;
bool plugin_enabled;
+ bool fake_insn;
struct TCGOp *insn_start;
void *host_addr[2];
-} DisasContextBase;
+
+ /*
+ * Record insn data that we cannot read directly from host memory.
+ * There are only two reasons we cannot use host memory:
+ * (1) We are executing from I/O,
+ * (2) We are executing a synthetic instruction (s390x EX).
+ * In both cases we need record exactly one instruction,
+ * and thus the maximum amount of data we record is limited.
+ */
+ int record_start;
+ int record_len;
+ uint8_t record[32];
+};
/**
* TranslatorOps:
@@ -125,7 +135,7 @@ typedef struct TranslatorOps {
void (*insn_start)(DisasContextBase *db, CPUState *cpu);
void (*translate_insn)(DisasContextBase *db, CPUState *cpu);
void (*tb_stop)(DisasContextBase *db, CPUState *cpu);
- void (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f);
+ bool (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f);
} TranslatorOps;
/**
@@ -185,14 +195,14 @@ bool translator_io_start(DisasContextBase *db);
* the relevant information at translation time.
*/
-uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
-uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
-uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
-uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
+uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc);
+uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc);
+uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc);
+uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc);
static inline uint16_t
translator_lduw_swap(CPUArchState *env, DisasContextBase *db,
- abi_ptr pc, bool do_swap)
+ vaddr pc, bool do_swap)
{
uint16_t ret = translator_lduw(env, db, pc);
if (do_swap) {
@@ -203,7 +213,7 @@ translator_lduw_swap(CPUArchState *env, DisasContextBase *db,
static inline uint32_t
translator_ldl_swap(CPUArchState *env, DisasContextBase *db,
- abi_ptr pc, bool do_swap)
+ vaddr pc, bool do_swap)
{
uint32_t ret = translator_ldl(env, db, pc);
if (do_swap) {
@@ -214,7 +224,7 @@ translator_ldl_swap(CPUArchState *env, DisasContextBase *db,
static inline uint64_t
translator_ldq_swap(CPUArchState *env, DisasContextBase *db,
- abi_ptr pc, bool do_swap)
+ vaddr pc, bool do_swap)
{
uint64_t ret = translator_ldq(env, db, pc);
if (do_swap) {
@@ -224,17 +234,42 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db,
}
/**
- * translator_fake_ldb - fake instruction load
- * @insn8: byte of instruction
- * @pc: program counter of instruction
+ * translator_fake_ld - fake instruction load
+ * @db: Disassembly context
+ * @data: bytes of instruction
+ * @len: number of bytes
*
* This is a special case helper used where the instruction we are
* about to translate comes from somewhere else (e.g. being
* re-synthesised for s390x "ex"). It ensures we update other areas of
* the translator with details of the executed instruction.
*/
-void translator_fake_ldb(uint8_t insn8, abi_ptr pc);
+void translator_fake_ld(DisasContextBase *db, const void *data, size_t len);
+
+/**
+ * translator_st
+ * @db: disassembly context
+ * @dest: address to copy into
+ * @addr: virtual address within TB
+ * @len: length
+ *
+ * Copy @len bytes from @addr into @dest.
+ * All bytes must have been read during translation.
+ * Return true on success or false on failure.
+ */
+bool translator_st(const DisasContextBase *db, void *dest,
+ vaddr addr, size_t len);
+
+/**
+ * translator_st_len
+ * @db: disassembly context
+ *
+ * Return the number of bytes available to copy from the
+ * current translation block with translator_st.
+ */
+size_t translator_st_len(const DisasContextBase *db);
+#ifdef COMPILING_PER_TARGET
/*
* Return whether addr is on the same page as where disassembly started.
* Translators can use this to enforce the rule that only single-insn
@@ -244,5 +279,6 @@ static inline bool is_same_page(const DisasContextBase *db, vaddr addr)
{
return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0;
}
+#endif
#endif /* EXEC__TRANSLATOR_H */
diff --git a/include/glib-compat.h b/include/glib-compat.h
index 43a562974d..86be439ba0 100644
--- a/include/glib-compat.h
+++ b/include/glib-compat.h
@@ -19,12 +19,12 @@
/* Ask for warnings for anything that was marked deprecated in
* the defined version, or before. It is a candidate for rewrite.
*/
-#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56
+#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66
/* Ask for warnings if code tries to use function that did not
* exist in the defined version. These risk breaking builds
*/
-#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56
+#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -105,29 +105,6 @@ static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size)
}
#define g_memdup2(m, s) g_memdup2_qemu(m, s)
-#if defined(G_OS_UNIX)
-/*
- * Note: The fallback implementation is not MT-safe, and it returns a copy of
- * the libc passwd (must be g_free() after use) but not the content. Because of
- * these important differences the caller must be aware of, it's not #define for
- * GLib API substitution.
- */
-static inline struct passwd *
-g_unix_get_passwd_entry_qemu(const gchar *user_name, GError **error)
-{
-#if GLIB_CHECK_VERSION(2, 64, 0)
- return g_unix_get_passwd_entry(user_name, error);
-#else
- struct passwd *p = getpwnam(user_name);
- if (!p) {
- g_set_error_literal(error, G_UNIX_ERROR, 0, g_strerror(errno));
- return NULL;
- }
- return (struct passwd *)g_memdup(p, sizeof(*p));
-#endif
-}
-#endif /* G_OS_UNIX */
-
static inline bool
qemu_g_test_slow(void)
{
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 46b99a7ea5..bb398e8237 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -28,6 +28,7 @@
#include "exec/memattrs.h"
#include "exec/mmu-access-type.h"
#include "exec/tlb-common.h"
+#include "qapi/qapi-types-machine.h"
#include "qapi/qapi-types-run-state.h"
#include "qemu/bitmap.h"
#include "qemu/rcu_queue.h"
@@ -84,6 +85,12 @@ DECLARE_CLASS_CHECKERS(CPUClass, CPU,
typedef struct CPUWatchpoint CPUWatchpoint;
+/* see physmem.c */
+struct CPUAddressSpace;
+
+/* see accel/tcg/tb-jmp-cache.h */
+struct CPUJumpCache;
+
/* see accel-cpu.h */
struct AccelCPUClass;
@@ -338,12 +345,20 @@ typedef union IcountDecr {
} u16;
} IcountDecr;
-/*
- * Elements of CPUState most efficiently accessed from CPUArchState,
- * via small negative offsets.
+/**
+ * CPUNegativeOffsetState: Elements of CPUState most efficiently accessed
+ * from CPUArchState, via small negative offsets.
+ * @can_do_io: True if memory-mapped IO is allowed.
+ * @plugin_mem_cbs: active plugin memory callbacks
*/
typedef struct CPUNegativeOffsetState {
CPUTLB tlb;
+#ifdef CONFIG_PLUGIN
+ /*
+ * The callback pointer are accessed via TCG (see gen_empty_mem_helper).
+ */
+ GArray *plugin_mem_cbs;
+#endif
IcountDecr icount_decr;
bool can_do_io;
} CPUNegativeOffsetState;
@@ -400,7 +415,6 @@ struct qemu_work_item;
* @crash_occurred: Indicates the OS reported a crash (panic) for this CPU
* @singlestep_enabled: Flags for single-stepping.
* @icount_extra: Instructions until next timer event.
- * @neg.can_do_io: True if memory-mapped IO is allowed.
* @cpu_ases: Pointer to array of CPUAddressSpaces (which define the
* AddressSpaces this CPU has)
* @num_ases: number of CPUAddressSpaces in @cpu_ases
@@ -416,7 +430,6 @@ struct qemu_work_item;
* @kvm_fd: vCPU file descriptor for KVM.
* @work_mutex: Lock to prevent multiple access to @work_list.
* @work_list: List of pending asynchronous work.
- * @plugin_mem_cbs: active plugin memory callbacks
* @plugin_state: per-CPU plugin state
* @ignore_memory_transaction_failures: Cached copy of the MachineState
* flag of the same name: allows the board to suppress calling of the
@@ -472,12 +485,12 @@ struct CPUState {
QemuMutex work_mutex;
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
- CPUAddressSpace *cpu_ases;
+ struct CPUAddressSpace *cpu_ases;
int num_ases;
AddressSpace *as;
MemoryRegion *memory;
- CPUJumpCache *tb_jmp_cache;
+ struct CPUJumpCache *tb_jmp_cache;
GArray *gdb_regs;
int gdb_num_regs;
@@ -511,11 +524,6 @@ struct CPUState {
QemuLockCnt in_ioctl_lock;
#ifdef CONFIG_PLUGIN
- /*
- * The callback pointer stays in the main CPUState as it is
- * accessed via TCG (see gen_empty_mem_helper).
- */
- GArray *plugin_mem_cbs;
CPUPluginState *plugin_state;
#endif
@@ -1112,23 +1120,6 @@ void cpu_watchpoint_remove_all(CPUState *cpu, int mask);
#endif
/**
- * cpu_plugin_mem_cbs_enabled() - are plugin memory callbacks enabled?
- * @cs: CPUState pointer
- *
- * The memory callbacks are installed if a plugin has instrumented an
- * instruction for memory. This can be useful to know if you want to
- * force a slow path for a series of memory accesses.
- */
-static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu)
-{
-#ifdef CONFIG_PLUGIN
- return !!cpu->plugin_mem_cbs;
-#else
- return false;
-#endif
-}
-
-/**
* cpu_get_address_space:
* @cpu: CPU to get address space from
* @asidx: index identifying which address space to get
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h
index 4dc30dcb4d..b006f16b8d 100644
--- a/include/hw/i386/x86.h
+++ b/include/hw/i386/x86.h
@@ -18,8 +18,10 @@
#define HW_I386_X86_H
#include "exec/hwaddr.h"
+#include "exec/memory.h"
#include "hw/boards.h"
+#include "hw/i386/topology.h"
#include "hw/intc/ioapic.h"
#include "hw/isa/isa.h"
#include "qom/object.h"
@@ -52,6 +54,18 @@ struct X86MachineState {
GMappedFile *initrd_mapped_file;
HotplugHandler *acpi_dev;
+ /*
+ * Map the whole BIOS just underneath the 4 GiB address boundary. Only used
+ * in the ROM (-bios) case.
+ */
+ MemoryRegion bios;
+
+ /*
+ * Map the upper 128 KiB of the BIOS just underneath the 1 MiB address
+ * boundary.
+ */
+ MemoryRegion isa_bios;
+
/* RAM information (sizes, addresses, configuration): */
ram_addr_t below_4g_mem_size, above_4g_mem_size;
@@ -96,16 +110,11 @@ struct X86MachineState {
#define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86")
OBJECT_DECLARE_TYPE(X86MachineState, X86MachineClass, X86_MACHINE)
-uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms,
+void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms);
+uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms,
unsigned int cpu_index);
-void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp);
void x86_cpus_init(X86MachineState *pcms, int default_cpu_version);
-CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms,
- unsigned cpu_index);
-int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx);
-const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms);
-CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx);
void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count);
void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
@@ -116,7 +125,9 @@ void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
-void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
+void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory,
+ MemoryRegion *bios, bool read_only);
+void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware,
MemoryRegion *rom_memory, bool isapc_ram_fw);
void x86_load_linux(X86MachineState *x86ms,
diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h
index c412575775..1f2420231f 100644
--- a/include/hw/intc/i8259.h
+++ b/include/hw/intc/i8259.h
@@ -3,6 +3,8 @@
/* i8259.c */
+typedef struct PICCommonState PICCommonState;
+
extern PICCommonState *isa_pic;
/*
diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h
index a0a46b888c..410c6e1121 100644
--- a/include/hw/intc/loongarch_extioi.h
+++ b/include/hw/intc/loongarch_extioi.h
@@ -39,6 +39,7 @@
#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET)
#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET)
#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET)
+#define EXTIOI_SIZE 0x800
typedef struct ExtIOICore {
uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT];
diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongson_ipi.h
index 1c1e834849..2c0e8820f5 100644
--- a/include/hw/intc/loongarch_ipi.h
+++ b/include/hw/intc/loongson_ipi.h
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * LoongArch ipi interrupt header files
+ * Loongson ipi interrupt header files
*
* Copyright (C) 2021 Loongson Technology Corporation Limited
*/
-#ifndef HW_LOONGARCH_IPI_H
-#define HW_LOONGARCH_IPI_H
+#ifndef HW_LOONGSON_IPI_H
+#define HW_LOONGSON_IPI_H
#include "hw/sysbus.h"
@@ -30,8 +30,8 @@
#define IPI_MBX_NUM 4
-#define TYPE_LOONGARCH_IPI "loongarch_ipi"
-OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI)
+#define TYPE_LOONGSON_IPI "loongson_ipi"
+OBJECT_DECLARE_SIMPLE_TYPE(LoongsonIPI, LOONGSON_IPI)
typedef struct IPICore {
uint32_t status;
@@ -43,7 +43,7 @@ typedef struct IPICore {
qemu_irq irq;
} IPICore;
-struct LoongArchIPI {
+struct LoongsonIPI {
SysBusDevice parent_obj;
MemoryRegion ipi_iocsr_mem;
MemoryRegion ipi64_iocsr_mem;
diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h
new file mode 100644
index 0000000000..b3b870df1f
--- /dev/null
+++ b/include/hw/loongarch/boot.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Definitions for LoongArch boot.
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGARCH_BOOT_H
+#define HW_LOONGARCH_BOOT_H
+
+/* UEFI 2.10 */
+#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249
+#define EFI_2_100_SYSTEM_TABLE_REVISION ((2<<16) | (100))
+#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION
+#define EFI_SYSTEM_TABLE_REVISION EFI_2_100_SYSTEM_TABLE_REVISION
+
+#define FW_VERSION 0x1
+#define FW_PATCHLEVEL 0x0
+
+typedef struct {
+ uint8_t b[16];
+} efi_guid_t QEMU_ALIGNED(8);
+
+#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \
+ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, \
+ (c) & 0xff, ((c) >> 8) & 0xff, d } }
+
+#define LINUX_EFI_BOOT_MEMMAP_GUID \
+ EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \
+ 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4)
+
+#define LINUX_EFI_INITRD_MEDIA_GUID \
+ EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \
+ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
+
+#define DEVICE_TREE_GUID \
+ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, \
+ 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
+
+struct efi_config_table {
+ efi_guid_t guid;
+ uint64_t *ptr;
+ const char name[16];
+};
+
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t headersize;
+ uint32_t crc32;
+ uint32_t reserved;
+} efi_table_hdr_t;
+
+struct efi_configuration_table {
+ efi_guid_t guid;
+ void *table;
+};
+
+struct efi_system_table {
+ efi_table_hdr_t hdr;
+ uint64_t fw_vendor; /* physical addr of CHAR16 vendor string */
+ uint32_t fw_revision;
+ uint64_t con_in_handle;
+ uint64_t *con_in;
+ uint64_t con_out_handle;
+ uint64_t *con_out;
+ uint64_t stderr_handle;
+ uint64_t stderr_placeholder;
+ uint64_t *runtime;
+ uint64_t *boottime;
+ uint64_t nr_tables;
+ struct efi_configuration_table *tables;
+};
+
+typedef struct {
+ uint32_t type;
+ uint32_t pad;
+ uint64_t phys_addr;
+ uint64_t virt_addr;
+ uint64_t num_pages;
+ uint64_t attribute;
+} efi_memory_desc_t;
+
+struct efi_boot_memmap {
+ uint64_t map_size;
+ uint64_t desc_size;
+ uint32_t desc_ver;
+ uint64_t map_key;
+ uint64_t buff_size;
+ efi_memory_desc_t map[32];
+};
+
+struct efi_initrd {
+ uint64_t base;
+ uint64_t size;
+};
+
+struct loongarch_boot_info {
+ uint64_t ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+ uint64_t a0, a1, a2;
+};
+
+extern struct memmap_entry *memmap_table;
+extern unsigned memmap_entries;
+
+struct memmap_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+ uint32_t reserved;
+};
+
+void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info);
+
+#endif /* HW_LOONGARCH_BOOT_H */
diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h
index 252f7df7f4..2c4f5cf9c8 100644
--- a/include/hw/loongarch/virt.h
+++ b/include/hw/loongarch/virt.h
@@ -11,8 +11,9 @@
#include "target/loongarch/cpu.h"
#include "hw/boards.h"
#include "qemu/queue.h"
-#include "hw/intc/loongarch_ipi.h"
+#include "hw/intc/loongson_ipi.h"
#include "hw/block/flash.h"
+#include "hw/loongarch/boot.h"
#define LOONGARCH_MAX_CPUS 256
@@ -32,7 +33,11 @@
#define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN)
#define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN)
-struct LoongArchMachineState {
+#define COMMAND_LINE_SIZE 512
+
+#define FDT_BASE 0x100000
+
+struct LoongArchVirtMachineState {
/*< private >*/
MachineState parent_obj;
@@ -55,10 +60,10 @@ struct LoongArchMachineState {
MemoryRegion system_iocsr;
MemoryRegion iocsr_mem;
AddressSpace as_iocsr;
+ struct loongarch_boot_info bootinfo;
};
-#define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt")
-OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE)
-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams);
-void loongarch_acpi_setup(LoongArchMachineState *lams);
+#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
+OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE)
+void loongarch_acpi_setup(LoongArchVirtMachineState *lvms);
#endif
diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
index c1f81a5f13..d173998803 100644
--- a/include/hw/nvram/fw_cfg.h
+++ b/include/hw/nvram/fw_cfg.h
@@ -59,6 +59,8 @@ typedef struct fw_cfg_dma_access FWCfgDmaAccess;
typedef void (*FWCfgCallback)(void *opaque);
typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len);
+typedef struct FWCfgEntry FWCfgEntry;
+
struct FWCfgState {
/*< private >*/
SysBusDevice parent_obj;
diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h
index e753449593..cd7c9ec7bc 100644
--- a/include/hw/pci-host/ls7a.h
+++ b/include/hw/pci-host/ls7a.h
@@ -24,6 +24,8 @@
#define VIRT_PCH_REG_BASE 0x10000000UL
#define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE)
#define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL
+#define VIRT_PCH_REG_SIZE 0x400
+#define VIRT_PCH_MSI_SIZE 0x8
/*
* GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index 11f5a91bbb..5eddb90976 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -27,6 +27,9 @@
#include "hw/pci/pcie_sriov.h"
#include "hw/hotplug.h"
+typedef struct PCIEPort PCIEPort;
+typedef struct PCIESlot PCIESlot;
+
typedef enum {
/* these bits must match the bits in Slot Control/Status registers.
* PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h
index 4a9f0ea69d..4d8c0e0507 100644
--- a/include/hw/pci/pcie_aer.h
+++ b/include/hw/pci/pcie_aer.h
@@ -25,8 +25,23 @@
/* definitions which PCIExpressDevice uses */
+/* error */
+typedef struct PCIEAERErr {
+ uint32_t status; /* error status bits */
+ uint16_t source_id; /* bdf */
+
+#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */
+#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */
+#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */
+#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */
+ uint16_t flags;
+
+ uint32_t header[4]; /* TLP header */
+ uint32_t prefix[4]; /* TLP header prefix */
+} PCIEAERErr;
+
/* AER log */
-struct PCIEAERLog {
+typedef struct PCIEAERLog {
/* This structure is saved/loaded.
So explicitly size them instead of unsigned int */
@@ -48,11 +63,11 @@ struct PCIEAERLog {
/* Error log. log_max-sized array */
PCIEAERErr *log;
-};
+} PCIEAERLog;
/* aer error message: error signaling message has only error severity and
source id. See 2.2.8.3 error signaling messages */
-struct PCIEAERMsg {
+typedef struct PCIEAERMsg {
/*
* PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN
* = PCI_EXP_DEVCTL_{CERE, NFERE, FERE}
@@ -60,7 +75,7 @@ struct PCIEAERMsg {
uint32_t severity;
uint16_t source_id; /* bdf */
-};
+} PCIEAERMsg;
static inline bool
pcie_aer_msg_is_uncor(const PCIEAERMsg *msg)
@@ -69,21 +84,6 @@ pcie_aer_msg_is_uncor(const PCIEAERMsg *msg)
msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN;
}
-/* error */
-struct PCIEAERErr {
- uint32_t status; /* error status bits */
- uint16_t source_id; /* bdf */
-
-#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */
-#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */
-#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */
-#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */
- uint16_t flags;
-
- uint32_t header[4]; /* TLP header */
- uint32_t prefix[4]; /* TLP header prefix */
-};
-
extern const VMStateDescription vmstate_pcie_aer_log;
int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset,
diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h
index b77eb7bf58..450cbef6c2 100644
--- a/include/hw/pci/pcie_sriov.h
+++ b/include/hw/pci/pcie_sriov.h
@@ -15,17 +15,17 @@
#include "hw/pci/pci.h"
-struct PCIESriovPF {
+typedef struct PCIESriovPF {
uint16_t num_vfs; /* Number of virtual functions created */
uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */
const char *vfname; /* Reference to the device type used for the VFs */
PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */
-};
+} PCIESriovPF;
-struct PCIESriovVF {
+typedef struct PCIESriovVF {
PCIDevice *pf; /* Pointer back to owner physical function */
uint16_t vf_number; /* Logical VF number of this function */
-};
+} PCIESriovVF;
void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
const char *vfname, uint16_t vf_dev_id,
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 9228e96c87..5336728a23 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -294,6 +294,7 @@ struct DeviceState {
MemReentrancyGuard mem_reentrancy_guard;
};
+typedef struct DeviceListener DeviceListener;
struct DeviceListener {
void (*realize)(DeviceListener *listener, DeviceState *dev);
void (*unrealize)(DeviceListener *listener, DeviceState *dev);
diff --git a/include/hw/rtc/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h
index 97cec0b3e8..64893be151 100644
--- a/include/hw/rtc/mc146818rtc.h
+++ b/include/hw/rtc/mc146818rtc.h
@@ -55,6 +55,6 @@ MC146818RtcState *mc146818_rtc_init(ISABus *bus, int base_year,
qemu_irq intercept_irq);
void mc146818rtc_set_cmos_data(MC146818RtcState *s, int addr, int val);
int mc146818rtc_get_cmos_data(MC146818RtcState *s, int addr);
-void qmp_rtc_reset_reinjection(Error **errp);
+void rtc_reset_reinjection(MC146818RtcState *rtc);
#endif /* HW_RTC_MC146818RTC_H */
diff --git a/include/hw/s390x/adapter.h b/include/hw/s390x/adapter.h
index 7f1703508c..d4fadc4f7f 100644
--- a/include/hw/s390x/adapter.h
+++ b/include/hw/s390x/adapter.h
@@ -12,12 +12,12 @@
#ifndef S390X_ADAPTER_H
#define S390X_ADAPTER_H
-struct AdapterInfo {
+typedef struct AdapterInfo {
uint64_t ind_addr;
uint64_t summary_addr;
uint64_t ind_offset;
uint32_t summary_offset;
uint32_t adapter_id;
-};
+} AdapterInfo;
#endif
diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h
index ba72ee3dd2..8289e45837 100644
--- a/include/hw/s390x/css.h
+++ b/include/hw/s390x/css.h
@@ -333,4 +333,10 @@ static inline int ccw_dstream_read_buf(CcwDataStream *cds, void *buff, int len)
#define ccw_dstream_read(cds, v) ccw_dstream_read_buf((cds), &(v), sizeof(v))
#define ccw_dstream_write(cds, v) ccw_dstream_write_buf((cds), &(v), sizeof(v))
+/**
+ * true if (vmstate based) migration of the channel subsystem
+ * is enabled, false if it is disabled.
+ */
+extern bool css_migration_enabled;
+
#endif
diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h
index 3ffd575d8f..ff874e792d 100644
--- a/include/hw/s390x/event-facility.h
+++ b/include/hw/s390x/event-facility.h
@@ -203,6 +203,6 @@ struct SCLPEventFacilityClass {
bool (*event_pending)(SCLPEventFacility *ef);
};
-BusState *sclp_get_event_facility_bus(void);
+BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef);
#endif
diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h
index c1d46e78af..996864a34e 100644
--- a/include/hw/s390x/s390-virtio-ccw.h
+++ b/include/hw/s390x/s390-virtio-ccw.h
@@ -13,6 +13,7 @@
#include "hw/boards.h"
#include "qom/object.h"
+#include "hw/s390x/sclp.h"
#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
@@ -28,6 +29,8 @@ struct S390CcwMachineState {
bool dea_key_wrap;
bool pv;
uint8_t loadparm[8];
+
+ SCLPDevice *sclp;
};
#define S390_PTF_REASON_NONE (0x00 << 8)
@@ -43,7 +46,6 @@ struct S390CcwMachineClass {
/*< public >*/
bool ri_allowed;
bool cpu_model_allowed;
- bool css_migration_enabled;
bool hpage_1m_allowed;
int max_threads;
};
@@ -55,10 +57,4 @@ bool cpu_model_allowed(void);
/* 1M huge page mappings allowed by the machine */
bool hpage_1m_allowed(void);
-/**
- * Returns true if (vmstate based) migration of the channel subsystem
- * is enabled, false if it is disabled.
- */
-bool css_migration_enabled(void);
-
#endif
diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h
index 3907a13d07..bcb081def5 100644
--- a/include/hw/s390x/s390_flic.h
+++ b/include/hw/s390x/s390_flic.h
@@ -47,6 +47,7 @@ struct S390FLICState {
/* to limit AdapterRoutes.num_routes for compat */
uint32_t adapter_routes_max_batch;
bool ais_supported;
+ bool migration_enabled;
};
diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
index b405a387b6..d32f6180e0 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -221,8 +221,6 @@ static inline int sccb_data_len(SCCB *sccb)
return be16_to_cpu(sccb->h.length) - sizeof(sccb->h);
}
-
-void s390_sclp_init(void);
void sclp_service_interrupt(uint32_t sccb);
void raise_irq_cpu_hotplug(void);
int sclp_service_call(S390CPU *cpu, uint64_t sccb, uint32_t code);
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index b9da6c08ef..4cb1ab8645 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -115,6 +115,7 @@ typedef struct VFIODevice {
bool no_mmap;
bool ram_block_discard_allowed;
OnOffAuto enable_migration;
+ bool migration_events;
VFIODeviceOps *ops;
unsigned int num_irqs;
unsigned int num_regions;
@@ -133,7 +134,30 @@ struct VFIODeviceOps {
int (*vfio_hot_reset_multi)(VFIODevice *vdev);
void (*vfio_eoi)(VFIODevice *vdev);
Object *(*vfio_get_object)(VFIODevice *vdev);
- void (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f);
+
+ /**
+ * @vfio_save_config
+ *
+ * Save device config state
+ *
+ * @vdev: #VFIODevice for which to save the config
+ * @f: #QEMUFile where to send the data
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns zero to indicate success and negative for error
+ */
+ int (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f, Error **errp);
+
+ /**
+ * @vfio_load_config
+ *
+ * Load device config state
+ *
+ * @vdev: #VFIODevice for which to load the config
+ * @f: #QEMUFile where to get the data
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f);
};
@@ -148,7 +172,7 @@ typedef struct VFIOGroup {
} VFIOGroup;
typedef struct VFIODMABuf {
- QemuDmaBuf buf;
+ QemuDmaBuf *buf;
uint32_t pos_x, pos_y, pos_updates;
uint32_t hot_x, hot_y, hot_updates;
int dmabuf_id;
@@ -183,8 +207,8 @@ void vfio_spapr_container_deinit(VFIOContainer *container);
void vfio_disable_irqindex(VFIODevice *vbasedev, int index);
void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index);
-int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
- int action, int fd, Error **errp);
+bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
+ int action, int fd, Error **errp);
void vfio_region_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size);
uint64_t vfio_region_read(void *opaque,
@@ -198,14 +222,14 @@ void vfio_region_exit(VFIORegion *region);
void vfio_region_finalize(VFIORegion *region);
void vfio_reset_handler(void *opaque);
struct vfio_device_info *vfio_get_device_info(int fd);
-int vfio_attach_device(char *name, VFIODevice *vbasedev,
- AddressSpace *as, Error **errp);
+bool vfio_attach_device(char *name, VFIODevice *vbasedev,
+ AddressSpace *as, Error **errp);
void vfio_detach_device(VFIODevice *vbasedev);
int vfio_kvm_device_add_fd(int fd, Error **errp);
int vfio_kvm_device_del_fd(int fd, Error **errp);
-int vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp);
+bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp);
void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer);
extern const MemoryRegionOps vfio_region_ops;
@@ -250,13 +274,12 @@ vfio_devices_all_running_and_mig_active(const VFIOContainerBase *bcontainer);
bool
vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer);
int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap, hwaddr iova,
- hwaddr size);
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp);
int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova,
- uint64_t size, ram_addr_t ram_addr);
+ uint64_t size, ram_addr_t ram_addr, Error **errp);
/* Returns 0 on success, or a negative errno. */
-int vfio_device_get_name(VFIODevice *vbasedev, Error **errp);
+bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp);
void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp);
void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops,
DeviceState *dev, bool ram_discard);
diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h
index 3582d5f97a..2776481fc9 100644
--- a/include/hw/vfio/vfio-container-base.h
+++ b/include/hw/vfio/vfio-container-base.h
@@ -76,16 +76,15 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer,
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb);
-int vfio_container_add_section_window(VFIOContainerBase *bcontainer,
- MemoryRegionSection *section,
- Error **errp);
+bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
+ MemoryRegionSection *section,
+ Error **errp);
void vfio_container_del_section_window(VFIOContainerBase *bcontainer,
MemoryRegionSection *section);
int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer,
- bool start);
+ bool start, Error **errp);
int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap,
- hwaddr iova, hwaddr size);
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp);
void vfio_container_init(VFIOContainerBase *bcontainer,
VFIOAddressSpace *space,
@@ -111,29 +110,55 @@ struct VFIOIOMMUClass {
InterfaceClass parent_class;
/* basic feature */
- int (*setup)(VFIOContainerBase *bcontainer, Error **errp);
+ bool (*setup)(VFIOContainerBase *bcontainer, Error **errp);
int (*dma_map)(const VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
void *vaddr, bool readonly);
int (*dma_unmap)(const VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb);
- int (*attach_device)(const char *name, VFIODevice *vbasedev,
- AddressSpace *as, Error **errp);
+ bool (*attach_device)(const char *name, VFIODevice *vbasedev,
+ AddressSpace *as, Error **errp);
void (*detach_device)(VFIODevice *vbasedev);
+
/* migration feature */
+
+ /**
+ * @set_dirty_page_tracking
+ *
+ * Start or stop dirty pages tracking on VFIO container
+ *
+ * @bcontainer: #VFIOContainerBase on which to de/activate dirty
+ * page tracking
+ * @start: indicates whether to start or stop dirty pages tracking
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer,
- bool start);
+ bool start, Error **errp);
+ /**
+ * @query_dirty_bitmap
+ *
+ * Get bitmap of dirty pages from container
+ *
+ * @bcontainer: #VFIOContainerBase from which to get dirty pages
+ * @vbmap: #VFIOBitmap internal bitmap structure
+ * @iova: iova base address
+ * @size: size of iova range
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer,
- VFIOBitmap *vbmap,
- hwaddr iova, hwaddr size);
+ VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp);
/* PCI specific */
int (*pci_hot_reset)(VFIODevice *vbasedev, bool single);
/* SPAPR specific */
- int (*add_window)(VFIOContainerBase *bcontainer,
- MemoryRegionSection *section,
- Error **errp);
+ bool (*add_window)(VFIOContainerBase *bcontainer,
+ MemoryRegionSection *section,
+ Error **errp);
void (*del_window)(VFIOContainerBase *bcontainer,
MemoryRegionSection *section);
void (*release)(VFIOContainerBase *bcontainer);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index ed44cdad6b..7a59379f5a 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -169,7 +169,7 @@ struct VirtIOGPUBaseClass {
DEFINE_PROP_UINT32("yres", _state, _conf.yres, 800)
typedef struct VGPUDMABuf {
- QemuDmaBuf buf;
+ QemuDmaBuf *buf;
uint32_t scanout_id;
QTAILQ_ENTRY(VGPUDMABuf) next;
} VGPUDMABuf;
@@ -177,6 +177,7 @@ typedef struct VGPUDMABuf {
struct VirtIOGPU {
VirtIOGPUBase parent_obj;
+ uint8_t scanout_vmstate_version;
uint64_t conf_max_hostmem;
VirtQueue *ctrl_vq;
@@ -238,7 +239,7 @@ struct VhostUserGPU {
VhostUserBackend *vhost;
int vhost_gpu_fd; /* closed by the chardev */
CharBackend vhost_chr;
- QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS];
+ QemuDmaBuf *dmabuf[VIRTIO_GPU_MAX_SCANOUTS];
bool backend_blocked;
};
diff --git a/include/hw/xen/xen-legacy-backend.h b/include/hw/xen/xen-legacy-backend.h
index 2cca174778..979c4ea04c 100644
--- a/include/hw/xen/xen-legacy-backend.h
+++ b/include/hw/xen/xen-legacy-backend.h
@@ -66,18 +66,8 @@ static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev,
return xen_be_unmap_grant_refs(xendev, ptr, &ref, 1);
}
-/* actual backend drivers */
-extern struct XenDevOps xen_console_ops; /* xen_console.c */
-extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */
-extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */
-extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */
-#ifdef CONFIG_VIRTFS
-extern struct XenDevOps xen_9pfs_ops; /* xen-9p-backend.c */
-#endif
-extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */
-#ifdef CONFIG_USB_LIBUSB
-extern struct XenDevOps xen_usb_ops; /* xen-usb.c */
-#endif
+/* backend drivers not included in all machines */
+extern struct XenDevOps xen_framebuffer_ops; /* xenfb.c */
/* configuration (aka xenbus setup) */
void xen_config_cleanup(void);
diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h
index ddad4b9f36..fdf84f47af 100644
--- a/include/hw/xen/xen_pvdev.h
+++ b/include/hw/xen/xen_pvdev.h
@@ -29,7 +29,6 @@ struct XenDevOps {
const char *node);
void (*frontend_changed)(struct XenLegacyDevice *xendev,
const char *node);
- int (*backend_register)(void);
};
struct XenLegacyDevice {
diff --git a/include/migration/colo.h b/include/migration/colo.h
index eaac07f26d..43222ef5ae 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -49,7 +49,7 @@ void colo_checkpoint_delay_set(void);
*
* Called with BQL locked, may temporary release BQL.
*/
-int coroutine_fn colo_incoming_co(void);
+void coroutine_fn colo_incoming_co(void);
void colo_shutdown(void);
#endif
diff --git a/include/migration/misc.h b/include/migration/misc.h
index c9e200f4eb..bfadc5613b 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -45,12 +45,6 @@ bool migrate_ram_is_ignored(RAMBlock *block);
/* migration/block.c */
-#ifdef CONFIG_LIVE_BLOCK_MIGRATION
-void blk_mig_init(void);
-#else
-static inline void blk_mig_init(void) {}
-#endif
-
AnnounceParameters *migrate_announce_params(void);
/* migration/savevm.c */
@@ -103,7 +97,7 @@ void migration_add_notifier_mode(NotifierWithReturn *notify,
void migration_remove_notifier(NotifierWithReturn *notify);
bool migration_is_running(void);
-void migration_file_set_error(int err);
+void migration_file_set_error(int ret, Error *err);
/* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */
bool migration_in_incoming_postcopy(void);
diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h
index d78e979f05..b679aaebbf 100644
--- a/include/monitor/hmp-target.h
+++ b/include/monitor/hmp-target.h
@@ -25,11 +25,10 @@
#ifndef MONITOR_HMP_TARGET_H
#define MONITOR_HMP_TARGET_H
-#include "cpu.h"
-
-#define MD_TLONG 0
-#define MD_I32 1
+typedef struct MonitorDef MonitorDef;
+#ifdef COMPILING_PER_TARGET
+#include "cpu.h"
struct MonitorDef {
const char *name;
int offset;
@@ -37,6 +36,10 @@ struct MonitorDef {
int val);
int type;
};
+#endif
+
+#define MD_TLONG 0
+#define MD_I32 1
const MonitorDef *target_monitor_defs(void);
int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval);
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index f4cf8f6717..954f3c83ad 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -180,5 +180,6 @@ void hmp_ioport_write(Monitor *mon, const QDict *qdict);
void hmp_boot_set(Monitor *mon, const QDict *qdict);
void hmp_info_mtree(Monitor *mon, const QDict *qdict);
void hmp_info_cryptodev(Monitor *mon, const QDict *qdict);
+void hmp_dumpdtb(Monitor *mon, const QDict *qdict);
#endif
diff --git a/include/net/announce.h b/include/net/announce.h
index 3d90c83c23..72e7e501f7 100644
--- a/include/net/announce.h
+++ b/include/net/announce.h
@@ -12,12 +12,12 @@
#include "qapi/qapi-types-net.h"
#include "qemu/timer.h"
-struct AnnounceTimer {
+typedef struct AnnounceTimer {
QEMUTimer *tm;
AnnounceParameters params;
QEMUClockType type;
int round;
-};
+} AnnounceTimer;
/* Returns: update the timer to the next time point */
int64_t qemu_announce_timer_step(AnnounceTimer *timer);
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index 97806811ee..1cf288445f 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -92,17 +92,14 @@ long slow_bitmap_count_one(const unsigned long *bitmap, long nbits);
static inline unsigned long *bitmap_try_new(long nbits)
{
- long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
- return g_try_malloc0(len);
+ long nelem = BITS_TO_LONGS(nbits);
+ return g_try_new0(unsigned long, nelem);
}
static inline unsigned long *bitmap_new(long nbits)
{
- unsigned long *ptr = bitmap_try_new(nbits);
- if (ptr == NULL) {
- abort();
- }
- return ptr;
+ long nelem = BITS_TO_LONGS(nbits);
+ return g_new0(unsigned long, nelem);
}
static inline void bitmap_zero(unsigned long *dst, long nbits)
@@ -265,10 +262,10 @@ unsigned long bitmap_find_next_zero_area(unsigned long *map,
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;
+ long new_nelem = BITS_TO_LONGS(new_nbits);
+ unsigned long *ptr = g_renew(unsigned long, old, new_nelem);
+ bitmap_clear(ptr, old_nbits, new_nbits - old_nbits);
+ return ptr;
}
void bitmap_to_le(unsigned long *dst, const unsigned long *src,
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
index e6aff45301..ff3084538b 100644
--- a/include/qemu/coroutine.h
+++ b/include/qemu/coroutine.h
@@ -84,6 +84,8 @@ static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex)
mutex->holder == qemu_coroutine_self());
}
+#include "qemu/lockable.h"
+
/**
* CoQueues are a mechanism to queue coroutines in order to continue executing
* them later. They are similar to condition variables, but they need help
@@ -281,8 +283,6 @@ void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size);
*/
void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size);
-#include "qemu/lockable.h"
-
/**
* Sends a (part of) iovec down a socket, yielding when the socket is full, or
* Receives data into a (part of) iovec from a socket,
diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 92c927a6a3..741dade7cf 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -187,9 +187,39 @@ char *freq_to_str(uint64_t freq_hz);
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")
-bool buffer_is_zero(const void *buf, size_t len);
+/*
+ * Check if a buffer is all zeroes.
+ */
+
+bool buffer_is_zero_ool(const void *vbuf, size_t len);
+bool buffer_is_zero_ge256(const void *vbuf, size_t len);
bool test_buffer_is_zero_next_accel(void);
+static inline bool buffer_is_zero_sample3(const char *buf, size_t len)
+{
+ /*
+ * For any reasonably sized buffer, these three samples come from
+ * three different cachelines. In qemu-img usage, we find that
+ * each byte eliminates more than half of all buffer testing.
+ * It is therefore critical to performance that the byte tests
+ * short-circuit, so that we do not pull in additional cache lines.
+ * Do not "optimize" this to !(a | b | c).
+ */
+ return !buf[0] && !buf[len - 1] && !buf[len / 2];
+}
+
+#ifdef __OPTIMIZE__
+static inline bool buffer_is_zero(const void *buf, size_t len)
+{
+ return (__builtin_constant_p(len) && len >= 256
+ ? buffer_is_zero_sample3(buf, len) &&
+ buffer_is_zero_ge256(buf, len)
+ : buffer_is_zero_ool(buf, len));
+}
+#else
+#define buffer_is_zero buffer_is_zero_ool
+#endif
+
/*
* Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128)
* Input is limited to 14-bit numbers
diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h
index 9823220446..62110d2eb7 100644
--- a/include/qemu/lockable.h
+++ b/include/qemu/lockable.h
@@ -18,11 +18,11 @@
typedef void QemuLockUnlockFunc(void *);
-struct QemuLockable {
+typedef struct QemuLockable {
void *object;
QemuLockUnlockFunc *lock;
QemuLockUnlockFunc *unlock;
-};
+} QemuLockable;
static inline __attribute__((__always_inline__)) QemuLockable *
qemu_make_lockable(void *x, QemuLockable *lockable)
diff --git a/include/qemu/log.h b/include/qemu/log.h
index df59bfabcd..e10e24cd4f 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -36,6 +36,7 @@ bool qemu_log_separate(void);
#define LOG_STRACE (1 << 19)
#define LOG_PER_THREAD (1 << 20)
#define CPU_LOG_TB_VPU (1 << 21)
+#define LOG_TB_OP_PLUGIN (1 << 22)
/* Lock/unlock output. */
diff --git a/include/qemu/option.h b/include/qemu/option.h
index b349828782..01e673ae03 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -54,6 +54,8 @@ enum QemuOptType {
QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */
};
+typedef struct QemuOpt QemuOpt;
+
typedef struct QemuOptDesc {
const char *name;
enum QemuOptType type;
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 41db748eda..bc5aef979e 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -67,16 +67,33 @@ union qemu_plugin_cb_sig {
};
enum plugin_dyn_cb_type {
- PLUGIN_CB_INSN,
- PLUGIN_CB_MEM,
- PLUGIN_N_CB_TYPES,
+ PLUGIN_CB_REGULAR,
+ PLUGIN_CB_COND,
+ PLUGIN_CB_MEM_REGULAR,
+ PLUGIN_CB_INLINE_ADD_U64,
+ PLUGIN_CB_INLINE_STORE_U64,
};
-enum plugin_dyn_cb_subtype {
- PLUGIN_CB_REGULAR,
- PLUGIN_CB_REGULAR_R,
- PLUGIN_CB_INLINE,
- PLUGIN_N_CB_SUBTYPES,
+struct qemu_plugin_regular_cb {
+ union qemu_plugin_cb_sig f;
+ TCGHelperInfo *info;
+ void *userp;
+ enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_inline_cb {
+ qemu_plugin_u64 entry;
+ uint64_t imm;
+ enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_conditional_cb {
+ union qemu_plugin_cb_sig f;
+ TCGHelperInfo *info;
+ void *userp;
+ qemu_plugin_u64 entry;
+ enum qemu_plugin_cond cond;
+ uint64_t imm;
};
/*
@@ -85,33 +102,24 @@ enum plugin_dyn_cb_subtype {
* instance of a callback to be called upon the execution of a particular TB.
*/
struct qemu_plugin_dyn_cb {
- union qemu_plugin_cb_sig f;
- void *userp;
- enum plugin_dyn_cb_subtype type;
- /* @rw applies to mem callbacks only (both regular and inline) */
- enum qemu_plugin_mem_rw rw;
- /* fields specific to each dyn_cb type go here */
+ enum plugin_dyn_cb_type type;
union {
- struct {
- qemu_plugin_u64 entry;
- enum qemu_plugin_op op;
- uint64_t imm;
- } inline_insn;
+ struct qemu_plugin_regular_cb regular;
+ struct qemu_plugin_conditional_cb cond;
+ struct qemu_plugin_inline_cb inline_insn;
};
};
/* Internal context for instrumenting an instruction */
struct qemu_plugin_insn {
- GByteArray *data;
uint64_t vaddr;
- void *haddr;
- GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
+ GArray *insn_cbs;
+ GArray *mem_cbs;
+ uint8_t len;
bool calls_helpers;
/* if set, the instruction calls helpers that might access guest memory */
bool mem_helper;
-
- bool mem_only;
};
/* A scoreboard is an array of values, indexed by vcpu_index */
@@ -120,81 +128,18 @@ struct qemu_plugin_scoreboard {
QLIST_ENTRY(qemu_plugin_scoreboard) entry;
};
-/*
- * qemu_plugin_insn allocate and cleanup functions. We don't expect to
- * cleanup many of these structures. They are reused for each fresh
- * translation.
- */
-
-static inline void qemu_plugin_insn_cleanup_fn(gpointer data)
-{
- struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
- g_byte_array_free(insn->data, true);
-}
-
-static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void)
-{
- int i, j;
- struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
- insn->data = g_byte_array_sized_new(4);
-
- for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
- for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
- insn->cbs[i][j] = g_array_new(false, false,
- sizeof(struct qemu_plugin_dyn_cb));
- }
- }
- return insn;
-}
-
/* Internal context for this TranslationBlock */
struct qemu_plugin_tb {
GPtrArray *insns;
size_t n;
- uint64_t vaddr;
- uint64_t vaddr2;
- void *haddr1;
- void *haddr2;
- bool mem_only;
/* if set, the TB calls helpers that might access guest memory */
bool mem_helper;
- GArray *cbs[PLUGIN_N_CB_SUBTYPES];
+ GArray *cbs;
};
/**
- * qemu_plugin_tb_insn_get(): get next plugin record for translation.
- * @tb: the internal tb context
- * @pc: address of instruction
- */
-static inline
-struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb,
- uint64_t pc)
-{
- struct qemu_plugin_insn *insn;
- int i, j;
-
- if (unlikely(tb->n == tb->insns->len)) {
- struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
- g_ptr_array_add(tb->insns, new_insn);
- }
- insn = g_ptr_array_index(tb->insns, tb->n++);
- g_byte_array_set_size(insn->data, 0);
- insn->calls_helpers = false;
- insn->mem_helper = false;
- insn->vaddr = pc;
-
- for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
- for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
- g_array_set_size(insn->cbs[i][j], 0);
- }
- }
-
- return insn;
-}
-
-/**
* struct CPUPluginState - per-CPU state for plugins
* @event_mask: plugin event bitmap. Modified only via async work.
*/
@@ -229,7 +174,7 @@ void qemu_plugin_add_dyn_cb_arr(GArray *arr);
static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu)
{
- cpu->plugin_mem_cbs = NULL;
+ cpu->neg.plugin_mem_cbs = NULL;
}
/**
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4fc6c3739b..95703d8fec 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -61,7 +61,7 @@ typedef uint64_t qemu_plugin_id_t;
extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
-#define QEMU_PLUGIN_VERSION 2
+#define QEMU_PLUGIN_VERSION 3
/**
* struct qemu_info_t - system information for plugins
@@ -263,6 +263,29 @@ enum qemu_plugin_mem_rw {
};
/**
+ * enum qemu_plugin_cond - condition to enable callback
+ *
+ * @QEMU_PLUGIN_COND_NEVER: false
+ * @QEMU_PLUGIN_COND_ALWAYS: true
+ * @QEMU_PLUGIN_COND_EQ: is equal?
+ * @QEMU_PLUGIN_COND_NE: is not equal?
+ * @QEMU_PLUGIN_COND_LT: is less than?
+ * @QEMU_PLUGIN_COND_LE: is less than or equal?
+ * @QEMU_PLUGIN_COND_GT: is greater than?
+ * @QEMU_PLUGIN_COND_GE: is greater than or equal?
+ */
+enum qemu_plugin_cond {
+ QEMU_PLUGIN_COND_NEVER,
+ QEMU_PLUGIN_COND_ALWAYS,
+ QEMU_PLUGIN_COND_EQ,
+ QEMU_PLUGIN_COND_NE,
+ QEMU_PLUGIN_COND_LT,
+ QEMU_PLUGIN_COND_LE,
+ QEMU_PLUGIN_COND_GT,
+ QEMU_PLUGIN_COND_GE,
+};
+
+/**
* typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback
* @id: unique plugin id
* @tb: opaque handle used for querying and instrumenting a block.
@@ -302,15 +325,41 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
void *userdata);
/**
+ * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @cb: callback function
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when a translated unit executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and
+ * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb.
+ * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and
+ * callback is never installed.
+ */
+QEMU_PLUGIN_API
+void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *userdata);
+
+/**
* enum qemu_plugin_op - describes an inline op
*
* @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t
- *
- * Note: currently only a single inline op is supported.
+ * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t
*/
enum qemu_plugin_op {
QEMU_PLUGIN_INLINE_ADD_U64,
+ QEMU_PLUGIN_INLINE_STORE_U64,
};
/**
@@ -345,6 +394,33 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
void *userdata);
/**
+ * qemu_plugin_register_vcpu_insn_exec_cond_cb() - conditional insn execution cb
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when an instruction executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and
+ * this function is equivalent to qemu_plugin_register_vcpu_insn_exec_cb.
+ * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and
+ * callback is never installed.
+ */
+QEMU_PLUGIN_API
+void qemu_plugin_register_vcpu_insn_exec_cond_cb(
+ struct qemu_plugin_insn *insn,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *userdata);
+
+/**
* qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu() - insn exec inline op
* @insn: the opaque qemu_plugin_insn handle for an instruction
* @op: the type of qemu_plugin_op (e.g. ADD_U64)
@@ -394,17 +470,16 @@ struct qemu_plugin_insn *
qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
/**
- * qemu_plugin_insn_data() - return ptr to instruction data
+ * qemu_plugin_insn_data() - copy instruction data
* @insn: opaque instruction handle from qemu_plugin_tb_get_insn()
+ * @dest: destination into which data is copied
+ * @len: length of dest
*
- * Note: data is only valid for duration of callback. See
- * qemu_plugin_insn_size() to calculate size of stream.
- *
- * Returns: pointer to a stream of bytes containing the value of this
- * instructions opcode.
+ * Returns the number of bytes copied, minimum of @len and insn size.
*/
QEMU_PLUGIN_API
-const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
+size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn,
+ void *dest, size_t len);
/**
* qemu_plugin_insn_size() - return size of instruction
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 50c277cf0b..9d222dc376 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -23,11 +23,9 @@
*/
typedef struct AccelCPUState AccelCPUState;
typedef struct AccelState AccelState;
-typedef struct AdapterInfo AdapterInfo;
typedef struct AddressSpace AddressSpace;
typedef struct AioContext AioContext;
typedef struct Aml Aml;
-typedef struct AnnounceTimer AnnounceTimer;
typedef struct ArchCPU ArchCPU;
typedef struct BdrvDirtyBitmap BdrvDirtyBitmap;
typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter;
@@ -38,29 +36,20 @@ typedef struct BusClass BusClass;
typedef struct BusState BusState;
typedef struct Chardev Chardev;
typedef struct Clock Clock;
-typedef struct CompatProperty CompatProperty;
typedef struct ConfidentialGuestSupport ConfidentialGuestSupport;
-typedef struct CPUAddressSpace CPUAddressSpace;
typedef struct CPUArchState CPUArchState;
typedef struct CPUPluginState CPUPluginState;
-typedef struct CpuInfoFast CpuInfoFast;
-typedef struct CPUJumpCache CPUJumpCache;
typedef struct CPUState CPUState;
-typedef struct CPUTLBEntryFull CPUTLBEntryFull;
-typedef struct DeviceListener DeviceListener;
typedef struct DeviceState DeviceState;
typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
+typedef struct DisasContextBase DisasContextBase;
typedef struct DisplayChangeListener DisplayChangeListener;
typedef struct DriveInfo DriveInfo;
typedef struct DumpState DumpState;
typedef struct Error Error;
typedef struct EventNotifier EventNotifier;
typedef struct FlatView FlatView;
-typedef struct FWCfgEntry FWCfgEntry;
-typedef struct FWCfgIoState FWCfgIoState;
-typedef struct FWCfgMemState FWCfgMemState;
typedef struct FWCfgState FWCfgState;
-typedef struct GraphicHwOps GraphicHwOps;
typedef struct HostMemoryBackend HostMemoryBackend;
typedef struct I2CBus I2CBus;
typedef struct I2SCodec I2SCodec;
@@ -80,31 +69,21 @@ typedef struct MemoryRegionSection MemoryRegionSection;
typedef struct MigrationIncomingState MigrationIncomingState;
typedef struct MigrationState MigrationState;
typedef struct Monitor Monitor;
-typedef struct MonitorDef MonitorDef;
typedef struct MSIMessage MSIMessage;
typedef struct NetClientState NetClientState;
typedef struct NetFilterState NetFilterState;
typedef struct NICInfo NICInfo;
-typedef struct NodeInfo NodeInfo;
-typedef struct NumaNodeMem NumaNodeMem;
typedef struct Object Object;
typedef struct ObjectClass ObjectClass;
typedef struct PCIBridge PCIBridge;
typedef struct PCIBus PCIBus;
typedef struct PCIDevice PCIDevice;
-typedef struct PCIEAERErr PCIEAERErr;
-typedef struct PCIEAERLog PCIEAERLog;
-typedef struct PCIEAERMsg PCIEAERMsg;
typedef struct PCIEPort PCIEPort;
typedef struct PCIESlot PCIESlot;
-typedef struct PCIESriovPF PCIESriovPF;
-typedef struct PCIESriovVF PCIESriovVF;
typedef struct PCIExpressDevice PCIExpressDevice;
typedef struct PCIExpressHost PCIExpressHost;
typedef struct PCIHostDeviceAddress PCIHostDeviceAddress;
typedef struct PCIHostState PCIHostState;
-typedef struct PICCommonState PICCommonState;
-typedef struct PostcopyDiscardState PostcopyDiscardState;
typedef struct Property Property;
typedef struct PropertyInfo PropertyInfo;
typedef struct QBool QBool;
@@ -113,9 +92,7 @@ typedef struct QEMUBH QEMUBH;
typedef struct QemuConsole QemuConsole;
typedef struct QEMUCursor QEMUCursor;
typedef struct QEMUFile QEMUFile;
-typedef struct QemuLockable QemuLockable;
typedef struct QemuMutex QemuMutex;
-typedef struct QemuOpt QemuOpt;
typedef struct QemuOpts QemuOpts;
typedef struct QemuOptsList QemuOptsList;
typedef struct QEMUSGList QEMUSGList;
@@ -134,6 +111,7 @@ typedef struct SHPCDevice SHPCDevice;
typedef struct SSIBus SSIBus;
typedef struct TCGCPUOps TCGCPUOps;
typedef struct TCGHelperInfo TCGHelperInfo;
+typedef struct TaskState TaskState;
typedef struct TranslationBlock TranslationBlock;
typedef struct VirtIODevice VirtIODevice;
typedef struct Visitor Visitor;
diff --git a/include/qemu/uri.h b/include/qemu/uri.h
deleted file mode 100644
index 255e61f452..0000000000
--- a/include/qemu/uri.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * Summary: library of generic URI related routines
- * Description: library of generic URI related routines
- * Implements RFC 2396
- *
- * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
- *
- * 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
- * DANIEL VEILLARD 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.
- *
- * Except as contained in this notice, the name of Daniel Veillard shall not
- * be used in advertising or otherwise to promote the sale, use or other
- * dealings in this Software without prior written authorization from him.
- *
- * Author: Daniel Veillard
- **
- * Copyright (C) 2007 Red Hat, Inc.
- *
- * 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.1 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 <https://www.gnu.org/licenses/>.
- *
- * Authors:
- * Richard W.M. Jones <rjones@redhat.com>
- *
- * Utility functions to help parse and assemble query strings.
- */
-
-#ifndef QEMU_URI_H
-#define QEMU_URI_H
-
-/**
- * URI:
- *
- * A parsed URI reference. This is a struct containing the various fields
- * as described in RFC 2396 but separated for further processing.
- */
-typedef struct URI {
- char *scheme; /* the URI scheme */
- char *opaque; /* opaque part */
- char *authority; /* the authority part */
- char *server; /* the server part */
- char *user; /* the user part */
- int port; /* the port number */
- char *path; /* the path string */
- char *fragment; /* the fragment identifier */
- int cleanup; /* parsing potentially unclean URI */
- char *query; /* the query string (as it appears in the URI) */
-} URI;
-
-URI *uri_new(void);
-URI *uri_parse(const char *str);
-URI *uri_parse_raw(const char *str, int raw);
-int uri_parse_into(URI *uri, const char *str);
-char *uri_to_string(URI *uri);
-void uri_free(URI *uri);
-
-/* Single web service query parameter 'name=value'. */
-typedef struct QueryParam {
- char *name; /* Name (unescaped). */
- char *value; /* Value (unescaped). */
- int ignore; /* Ignore this field in qparam_get_query */
-} QueryParam;
-
-/* Set of parameters. */
-typedef struct QueryParams {
- int n; /* number of parameters used */
- int alloc; /* allocated space */
- QueryParam *p; /* array of parameters */
-} QueryParams;
-
-QueryParams *query_params_new(int init_alloc);
-QueryParams *query_params_parse(const char *query);
-void query_params_free(QueryParams *ps);
-
-#endif /* QEMU_URI_H */
diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h
index dd289af8dd..c2fa5a655d 100644
--- a/include/semihosting/uaccess.h
+++ b/include/semihosting/uaccess.h
@@ -17,6 +17,7 @@
#include "exec/cpu-common.h"
#include "exec/cpu-defs.h"
#include "exec/tswap.h"
+#include "exec/page-protection.h"
#define get_user_u64(val, addr) \
({ uint64_t val_ = 0; \
diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
index 8eab395934..eb601522f8 100644
--- a/include/sysemu/device_tree.h
+++ b/include/sysemu/device_tree.h
@@ -134,7 +134,6 @@ int qemu_fdt_add_path(void *fdt, const char *path);
} while (0)
void qemu_fdt_dumpdtb(void *fdt, int size);
-void hmp_dumpdtb(Monitor *mon, const QDict *qdict);
/**
* qemu_fdt_setprop_sized_cells_from_array:
diff --git a/include/sysemu/iommufd.h b/include/sysemu/iommufd.h
index 9af27ebd6c..293bfbe967 100644
--- a/include/sysemu/iommufd.h
+++ b/include/sysemu/iommufd.h
@@ -23,11 +23,11 @@ struct IOMMUFDBackend {
/*< public >*/
};
-int iommufd_backend_connect(IOMMUFDBackend *be, Error **errp);
+bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp);
void iommufd_backend_disconnect(IOMMUFDBackend *be);
-int iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
- Error **errp);
+bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
+ Error **errp);
void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id);
int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
ram_addr_t size, void *vaddr, bool readonly);
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index eaf801bc93..c31d9c7356 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -470,10 +470,11 @@ static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c)
}
}
+int kvm_irqchip_get_virq(KVMState *s);
void kvm_irqchip_release_virq(KVMState *s, int virq);
-int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter);
-int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint);
+void kvm_add_routing_entry(KVMState *s,
+ struct kvm_irq_routing_entry *entry);
int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n,
EventNotifier *rn, int virq);
diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h
index 825cfe86bc..0467614147 100644
--- a/include/sysemu/numa.h
+++ b/include/sysemu/numa.h
@@ -36,7 +36,7 @@ enum {
#define UINT16_BITS 16
-struct NodeInfo {
+typedef struct NodeInfo {
uint64_t node_mem;
struct HostMemoryBackend *node_memdev;
bool present;
@@ -45,12 +45,12 @@ struct NodeInfo {
uint8_t lb_info_provided;
uint16_t initiator;
uint8_t distance[MAX_NODES];
-};
+} NodeInfo;
-struct NumaNodeMem {
+typedef struct NumaNodeMem {
uint64_t node_mem;
uint64_t node_plugged_mem;
-};
+} NumaNodeMem;
struct HMAT_LB_Data {
uint8_t initiator;
diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h
index 10c2e3082a..1ec9e66752 100644
--- a/include/sysemu/xen-mapcache.h
+++ b/include/sysemu/xen-mapcache.h
@@ -18,8 +18,9 @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset,
void xen_map_cache_init(phys_offset_to_gaddr_t f,
void *opaque);
-uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size,
- uint8_t lock, bool dma);
+uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size,
+ uint8_t lock, bool dma,
+ bool is_write);
ram_addr_t xen_ram_addr_from_mapcache(void *ptr);
void xen_invalidate_map_cache_entry(uint8_t *buffer);
void xen_invalidate_map_cache(void);
@@ -33,10 +34,12 @@ static inline void xen_map_cache_init(phys_offset_to_gaddr_t f,
{
}
-static inline uint8_t *xen_map_cache(hwaddr phys_addr,
+static inline uint8_t *xen_map_cache(MemoryRegion *mr,
+ hwaddr phys_addr,
hwaddr size,
uint8_t lock,
- bool dma)
+ bool dma,
+ bool is_write)
{
abort();
}
diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h
index 7c27d6164a..909fe73afa 100644
--- a/include/tcg/helper-info.h
+++ b/include/tcg/helper-info.h
@@ -12,6 +12,9 @@
#ifdef CONFIG_TCG_INTERPRETER
#include <ffi.h>
#endif
+#include "tcg-target-reg-bits.h"
+
+#define MAX_CALL_IARGS 7
/*
* Describe the calling convention of a given argument type.
diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index 2d932a515e..009e2778c5 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -74,8 +74,8 @@ void tcg_gen_goto_tb(unsigned idx);
*/
void tcg_gen_lookup_and_goto_ptr(void);
-void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr);
-void tcg_gen_plugin_cb_end(void);
+void tcg_gen_plugin_cb(unsigned from);
+void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo);
/* 32 bit ops */
diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h
index 4db8a58c14..65553f5f97 100644
--- a/include/tcg/tcg-op-gvec-common.h
+++ b/include/tcg/tcg-op-gvec-common.h
@@ -183,6 +183,8 @@ typedef struct {
bool prefer_i64;
/* Load dest as a 3rd source operand. */
bool load_dest;
+ /* Write aofs as a 2nd dest operand. */
+ bool write_aofs;
} GVecGen3i;
typedef struct {
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index b80227fa1c..546eb49c11 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -197,8 +197,8 @@ DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
-DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT)
-DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT)
+DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)
+DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT)
/* Replicate ld/st ops for 32 and 64-bit guest addresses. */
DEF(qemu_ld_a32_i32, 1, 1, 1,
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 05a1912f8a..2a1c080bab 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -39,8 +39,6 @@
/* XXX: make safe guess about sizes */
#define MAX_OP_PER_INSTR 266
-#define MAX_CALL_IARGS 7
-
#define CPU_TEMP_BUF_NLONGS 128
#define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long))
@@ -355,8 +353,6 @@ typedef TCGv_ptr TCGv_env;
#define TCG_CALL_NO_SIDE_EFFECTS 0x0004
/* Helper is G_NORETURN. */
#define TCG_CALL_NO_RETURN 0x0008
-/* Helper is part of Plugins. */
-#define TCG_CALL_PLUGIN 0x0010
/* convenience version of most used call flags */
#define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS
@@ -541,6 +537,7 @@ struct TCGContext {
* space for instructions (for variable-instruction-length ISAs).
*/
struct qemu_plugin_tb *plugin_tb;
+ const struct DisasContextBase *plugin_db;
/* descriptor of the instruction being translated */
struct qemu_plugin_insn *plugin_insn;
@@ -854,19 +851,22 @@ typedef struct TCGTargetOpDef {
bool tcg_op_supported(TCGOpcode op);
-void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret);
-void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *);
-void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *);
-void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *,
- TCGTemp *, TCGTemp *);
-void tcg_gen_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret);
+void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *);
+void tcg_gen_call2(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *);
-void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call3(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *);
-void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call4(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *);
-void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call5(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *);
+void tcg_gen_call6(void *func, TCGHelperInfo *, TCGTemp *ret,
+ TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *,
+ TCGTemp *, TCGTemp *);
+void tcg_gen_call7(void *func, TCGHelperInfo *, TCGTemp *ret,
+ TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *,
+ TCGTemp *, TCGTemp *, TCGTemp *);
TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs);
void tcg_op_remove(TCGContext *s, TCGOp *op);
@@ -1071,5 +1071,6 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n)
}
bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned);
+void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs);
#endif /* TCG_H */
diff --git a/include/ui/console.h b/include/ui/console.h
index 0bc7a00ac0..a208a68b88 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -7,6 +7,7 @@
#include "qapi/qapi-types-ui.h"
#include "ui/input.h"
#include "ui/surface.h"
+#include "ui/dmabuf.h"
#define TYPE_QEMU_CONSOLE "qemu-console"
OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
@@ -185,25 +186,6 @@ struct QEMUGLParams {
int minor_ver;
};
-typedef struct QemuDmaBuf {
- int fd;
- uint32_t width;
- uint32_t height;
- uint32_t stride;
- uint32_t fourcc;
- uint64_t modifier;
- uint32_t texture;
- uint32_t x;
- uint32_t y;
- uint32_t backing_width;
- uint32_t backing_height;
- bool y0_top;
- void *sync;
- int fence_fd;
- bool allow_fences;
- bool draw_submitted;
-} QemuDmaBuf;
-
enum display_scanout {
SCANOUT_NONE,
SCANOUT_SURFACE,
diff --git a/include/ui/dmabuf.h b/include/ui/dmabuf.h
new file mode 100644
index 0000000000..dc74ba895a
--- /dev/null
+++ b/include/ui/dmabuf.h
@@ -0,0 +1,49 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QemuDmaBuf struct and helpers used for accessing its data
+ *
+ * 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 DMABUF_H
+#define DMABUF_H
+
+typedef struct QemuDmaBuf QemuDmaBuf;
+
+QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height,
+ uint32_t stride, uint32_t x,
+ uint32_t y, uint32_t backing_width,
+ uint32_t backing_height, uint32_t fourcc,
+ uint64_t modifier, int dmabuf_fd,
+ bool allow_fences, bool y0_top);
+void qemu_dmabuf_free(QemuDmaBuf *dmabuf);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuDmaBuf, qemu_dmabuf_free);
+
+int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf);
+int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf);
+void qemu_dmabuf_close(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf);
+uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf);
+uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf);
+bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf);
+void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf);
+int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf);
+bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf);
+bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf);
+void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture);
+void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd);
+void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync);
+void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted);
+void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd);
+
+#endif
diff --git a/include/exec/user/abitypes.h b/include/user/abitypes.h
index 3ec1969368..5c9a955631 100644
--- a/include/exec/user/abitypes.h
+++ b/include/user/abitypes.h
@@ -1,5 +1,5 @@
-#ifndef EXEC_USER_ABITYPES_H
-#define EXEC_USER_ABITYPES_H
+#ifndef USER_ABITYPES_H
+#define USER_ABITYPES_H
#ifndef CONFIG_USER_ONLY
#error Cannot include this header from system emulation
diff --git a/include/user/syscall-trace.h b/include/user/syscall-trace.h
index b48b2b2d0a..9bd7ca19c8 100644
--- a/include/user/syscall-trace.h
+++ b/include/user/syscall-trace.h
@@ -10,7 +10,7 @@
#ifndef SYSCALL_TRACE_H
#define SYSCALL_TRACE_H
-#include "exec/user/abitypes.h"
+#include "user/abitypes.h"
#include "gdbstub/user.h"
#include "qemu/plugin.h"
#include "trace/trace-root.h"
diff --git a/include/exec/user/thunk.h b/include/user/thunk.h
index 2ebfecf58e..2a2104b568 100644
--- a/include/exec/user/thunk.h
+++ b/include/user/thunk.h
@@ -17,11 +17,15 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef THUNK_H
-#define THUNK_H
+#ifndef USER_THUNK_H
+#define USER_THUNK_H
+
+#ifndef CONFIG_USER_ONLY
+#error Cannot include this header from system emulation
+#endif
#include "cpu.h"
-#include "exec/user/abitypes.h"
+#include "user/abitypes.h"
/* types enums definitions */
diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c
index db1a41e27f..ec665862d9 100644
--- a/linux-user/arm/cpu_loop.c
+++ b/linux-user/arm/cpu_loop.c
@@ -24,6 +24,7 @@
#include "cpu_loop-common.h"
#include "signal-common.h"
#include "semihosting/common-semi.h"
+#include "exec/page-protection.h"
#include "target/arm/syndrome.h"
#define get_user_code_u32(x, gaddr, env) \
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index f9461d2844..c1e1511ff2 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -8,6 +8,7 @@
#include "qemu.h"
#include "user/tswap-target.h"
+#include "exec/page-protection.h"
#include "user/guest-base.h"
#include "user-internals.h"
#include "signal-common.h"
@@ -968,24 +969,44 @@ const char *elf_hwcap2_str(uint32_t bit)
#endif /* TARGET_ARM */
#ifdef TARGET_SPARC
-#ifdef TARGET_SPARC64
-#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \
- | HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9)
-#ifndef TARGET_ABI32
-#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS )
+#ifndef TARGET_SPARC64
+# define ELF_CLASS ELFCLASS32
+# define ELF_ARCH EM_SPARC
+#elif defined(TARGET_ABI32)
+# define ELF_CLASS ELFCLASS32
+# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC)
#else
-#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC )
+# define ELF_CLASS ELFCLASS64
+# define ELF_ARCH EM_SPARCV9
#endif
-#define ELF_CLASS ELFCLASS64
-#define ELF_ARCH EM_SPARCV9
-#else
-#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \
- | HWCAP_SPARC_MULDIV)
-#define ELF_CLASS ELFCLASS32
-#define ELF_ARCH EM_SPARC
-#endif /* TARGET_SPARC64 */
+#include "elf.h"
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ /* There are not many sparc32 hwcap bits -- we have all of them. */
+ uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |
+ HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV;
+
+#ifdef TARGET_SPARC64
+ CPUSPARCState *env = cpu_env(thread_cpu);
+ uint32_t features = env->def.features;
+
+ r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS;
+ /* 32x32 multiply and divide are efficient. */
+ r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32;
+ /* We don't have an internal feature bit for this. */
+ r |= HWCAP_SPARC_POPC;
+ r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0;
+ r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0;
+ r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0;
+#endif
+
+ return r;
+}
static inline void init_thread(struct target_pt_regs *regs,
struct image_info *infop)
@@ -1866,8 +1887,8 @@ static inline void init_thread(struct target_pt_regs *regs,
static inline void init_thread(struct target_pt_regs *regs,
struct image_info *infop)
{
- regs->iaoq[0] = infop->entry;
- regs->iaoq[1] = infop->entry + 4;
+ regs->iaoq[0] = infop->entry | PRIV_USER;
+ regs->iaoq[1] = regs->iaoq[0] + 4;
regs->gr[23] = 0;
regs->gr[24] = infop->argv;
regs->gr[25] = infop->argc;
@@ -2361,7 +2382,7 @@ static bool zero_bss(abi_ulong start_bss, abi_ulong end_bss,
if (start_bss < align_bss) {
int flags = page_get_flags(start_bss);
- if (!(flags & PAGE_BITS)) {
+ if (!(flags & PAGE_RWX)) {
/*
* The whole address space of the executable was reserved
* at the start, therefore all pages will be VALID.
diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c
index d5232f37fe..bc093b8fe8 100644
--- a/linux-user/hppa/cpu_loop.c
+++ b/linux-user/hppa/cpu_loop.c
@@ -129,8 +129,8 @@ void cpu_loop(CPUHPPAState *env)
default:
env->gr[28] = ret;
/* We arrived here by faking the gateway page. Return. */
- env->iaoq_f = env->gr[31];
- env->iaoq_b = env->gr[31] + 4;
+ env->iaoq_f = env->gr[31] | PRIV_USER;
+ env->iaoq_b = env->iaoq_f + 4;
break;
case -QEMU_ERESTARTSYS:
case -QEMU_ESIGRETURN:
@@ -140,8 +140,8 @@ void cpu_loop(CPUHPPAState *env)
case EXCP_SYSCALL_LWS:
env->gr[21] = hppa_lws(env);
/* We arrived here by faking the gateway page. Return. */
- env->iaoq_f = env->gr[31];
- env->iaoq_b = env->gr[31] + 4;
+ env->iaoq_f = env->gr[31] | PRIV_USER;
+ env->iaoq_b = env->iaoq_f + 4;
break;
case EXCP_IMP:
force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f);
@@ -152,9 +152,9 @@ void cpu_loop(CPUHPPAState *env)
case EXCP_PRIV_OPR:
/* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */
if (env->cr[CR_IIR] == 0x04000000) {
- force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f);
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f);
} else {
- force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f);
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f);
}
break;
case EXCP_PRIV_REG:
@@ -170,7 +170,7 @@ void cpu_loop(CPUHPPAState *env)
force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f);
break;
case EXCP_BREAK:
- force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3);
+ force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f);
break;
case EXCP_DEBUG:
force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f);
diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c
index 682ba25922..f6f094c960 100644
--- a/linux-user/hppa/signal.c
+++ b/linux-user/hppa/signal.c
@@ -101,7 +101,9 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc)
cpu_hppa_loaded_fr0(env);
__get_user(env->iaoq_f, &sc->sc_iaoq[0]);
+ env->iaoq_f |= PRIV_USER;
__get_user(env->iaoq_b, &sc->sc_iaoq[1]);
+ env->iaoq_b |= PRIV_USER;
__get_user(env->cr[CR_SAR], &sc->sc_sar);
}
@@ -162,8 +164,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
unlock_user(fdesc, haddr, 0);
haddr = dest;
}
- env->iaoq_f = haddr;
- env->iaoq_b = haddr + 4;
+ env->iaoq_f = haddr | PRIV_USER;
+ env->iaoq_b = env->iaoq_f + 4;
env->psw_n = 0;
return;
diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h
index aacf3e9e02..4b84422a90 100644
--- a/linux-user/hppa/target_cpu.h
+++ b/linux-user/hppa/target_cpu.h
@@ -28,8 +28,8 @@ static inline void cpu_clone_regs_child(CPUHPPAState *env, target_ulong newsp,
/* Indicate child in return value. */
env->gr[28] = 0;
/* Return from the syscall. */
- env->iaoq_f = env->gr[31];
- env->iaoq_b = env->gr[31] + 4;
+ env->iaoq_f = env->gr[31] | PRIV_USER;
+ env->iaoq_b = env->iaoq_f + 4;
}
static inline void cpu_clone_regs_parent(CPUHPPAState *env, unsigned flags)
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index be3b9a68eb..4d09a72fad 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -20,6 +20,7 @@
#include <sys/shm.h>
#include "trace.h"
#include "exec/log.h"
+#include "exec/page-protection.h"
#include "qemu.h"
#include "user-internals.h"
#include "user-mmap.h"
@@ -117,7 +118,7 @@ static void shm_region_rm_complete(abi_ptr start, abi_ptr last)
static int validate_prot_to_pageflags(int prot)
{
int valid = PROT_READ | PROT_WRITE | PROT_EXEC | TARGET_PROT_SEM;
- int page_flags = (prot & PAGE_BITS) | PAGE_VALID;
+ int page_flags = (prot & PAGE_RWX) | PAGE_VALID;
#ifdef TARGET_AARCH64
{
@@ -959,8 +960,8 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
*/
if (ret != -1 && (flags & MAP_TYPE) != MAP_PRIVATE) {
CPUState *cpu = thread_cpu;
- if (!(cpu->tcg_cflags & CF_PARALLEL)) {
- cpu->tcg_cflags |= CF_PARALLEL;
+ if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
+ tcg_cflags_set(cpu, CF_PARALLEL);
tb_flush(cpu);
}
}
@@ -1399,8 +1400,8 @@ abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
* supported by the host -- anything that requires EXCP_ATOMIC will not
* be atomic with respect to an external process.
*/
- if (!(cpu->tcg_cflags & CF_PARALLEL)) {
- cpu->tcg_cflags |= CF_PARALLEL;
+ if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
+ tcg_cflags_set(cpu, CF_PARALLEL);
tb_flush(cpu);
}
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 4777856b52..2e90a97175 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -4,10 +4,11 @@
#include "cpu.h"
#include "exec/cpu_ldst.h"
-#include "exec/user/abitypes.h"
+#include "user/abitypes.h"
#include "syscall_defs.h"
#include "target_syscall.h"
+#include "accel/tcg/vcpu-state.h"
/*
* This is the size of the host kernel's sigset_t, needed where we make
@@ -95,7 +96,7 @@ struct emulated_sigtable {
target_siginfo_t info;
};
-typedef struct TaskState {
+struct TaskState {
pid_t ts_tid; /* tid (or pid) of this task */
#ifdef TARGET_ARM
# ifdef TARGET_ABI32
@@ -158,12 +159,7 @@ typedef struct TaskState {
/* Start time of task after system boot in clock ticks */
uint64_t start_boottime;
-} TaskState;
-
-static inline TaskState *get_task_state(CPUState *cs)
-{
- return cs->opaque;
-}
+};
abi_long do_brk(abi_ulong new_brk);
int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname,
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 05dc4afb52..63ac2df53b 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include "gdbstub/user.h"
+#include "exec/page-protection.h"
#include "hw/core/tcg-cpu-ops.h"
#include <sys/ucontext.h>
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 41659b63f5..b9b5a387b3 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -25,6 +25,7 @@
#include "qemu/plugin.h"
#include "tcg/startup.h"
#include "target_mman.h"
+#include "exec/page-protection.h"
#include <elf.h>
#include <endian.h>
#include <grp.h>
@@ -6462,7 +6463,7 @@ static abi_long do_prctl(CPUArchState *env, abi_long option, abi_long arg2,
case PR_GET_TID_ADDRESS:
{
- TaskState *ts = env_cpu(env)->opaque;
+ TaskState *ts = get_task_state(env_cpu(env));
return put_user_ual(ts->child_tidptr, arg2);
}
@@ -6582,8 +6583,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
* generate code for parallel execution and flush old translations.
* Do this now so that the copy gets CF_PARALLEL too.
*/
- if (!(cpu->tcg_cflags & CF_PARALLEL)) {
- cpu->tcg_cflags |= CF_PARALLEL;
+ if (!tcg_cflags_has(cpu, CF_PARALLEL)) {
+ tcg_cflags_set(cpu, CF_PARALLEL);
tb_flush(cpu);
}
@@ -8123,7 +8124,7 @@ static int open_self_maps_2(void *opaque, target_ulong guest_start,
static int open_self_maps_1(CPUArchState *env, int fd, bool smaps)
{
struct open_self_maps_data d = {
- .ts = env_cpu(env)->opaque,
+ .ts = get_task_state(env_cpu(env)),
.host_maps = read_self_maps(),
.fd = fd,
.smaps = smaps
diff --git a/linux-user/thunk.c b/linux-user/thunk.c
index 071aad4b5f..3cd19e79c6 100644
--- a/linux-user/thunk.c
+++ b/linux-user/thunk.c
@@ -20,7 +20,7 @@
#include "qemu/log.h"
#include "qemu.h"
-#include "exec/user/thunk.h"
+#include "user/thunk.h"
//#define DEBUG
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
index ce11d9e21c..5c7f173ceb 100644
--- a/linux-user/user-internals.h
+++ b/linux-user/user-internals.h
@@ -18,7 +18,7 @@
#ifndef LINUX_USER_USER_INTERNALS_H
#define LINUX_USER_USER_INTERNALS_H
-#include "exec/user/thunk.h"
+#include "user/thunk.h"
#include "exec/exec-all.h"
#include "exec/tb-flush.h"
#include "qemu/log.h"
diff --git a/meson.build b/meson.build
index 5db2dbc12e..a9de71d450 100644
--- a/meson.build
+++ b/meson.build
@@ -869,7 +869,7 @@ have_xen_pci_passthrough = get_option('xen_pci_passthrough') \
# When bumping glib minimum version, please check also whether to increase
# the _WIN32_WINNT setting in osdep.h according to the value from glib
-glib_req_ver = '>=2.56.0'
+glib_req_ver = '>=2.66.0'
glib_pc = dependency('glib-2.0', version: glib_req_ver, required: true,
method: 'pkg-config')
glib_cflags = []
@@ -910,20 +910,6 @@ if not cc.compiles('''
to the right pkg-config files for your build target.''')
endif
-# Silence clang warnings triggered by glib < 2.57.2
-if not cc.compiles('''
- #include <glib.h>
- typedef struct Foo {
- int i;
- } Foo;
- static void foo_free(Foo *f)
- {
- g_free(f);
- }
- G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free)
- int main(void) { return 0; }''', dependencies: glib_pc, args: ['-Wunused-function', '-Werror'])
- glib_cflags += cc.get_supported_arguments('-Wno-unused-function')
-endif
glib = declare_dependency(dependencies: [glib_pc, gmodule],
compile_args: glib_cflags,
version: glib_pc.version())
@@ -1858,6 +1844,34 @@ if numa.found() and not cc.links('''
endif
endif
+fdt = not_found
+fdt_opt = get_option('fdt')
+if fdt_opt == 'enabled' and get_option('wrap_mode') == 'nodownload'
+ fdt_opt = 'system'
+endif
+if fdt_opt in ['enabled', 'system'] or (fdt_opt == 'auto' and have_system)
+ fdt = cc.find_library('fdt', required: fdt_opt == 'system')
+ if fdt.found() and cc.links('''
+ #include <libfdt.h>
+ #include <libfdt_env.h>
+ int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''',
+ dependencies: fdt)
+ fdt_opt = 'system'
+ elif fdt_opt != 'system'
+ fdt_opt = get_option('wrap_mode') == 'nodownload' ? 'disabled' : 'internal'
+ fdt = not_found
+ else
+ error('system libfdt is too old (1.5.1 or newer required)')
+ endif
+endif
+if fdt_opt == 'internal'
+ assert(not fdt.found())
+ libfdt_proj = subproject('dtc', required: true,
+ default_options: ['tools=false', 'yaml=disabled',
+ 'python=disabled', 'default_library=static'])
+ fdt = libfdt_proj.get_variable('libfdt_dep')
+endif
+
rdma = not_found
if not get_option('rdma').auto() or have_system
libumad = cc.find_library('ibumad', required: get_option('rdma'))
@@ -2199,6 +2213,7 @@ config_host_data.set('CONFIG_BSD', host_os in bsd_oses)
config_host_data.set('CONFIG_CAPSTONE', capstone.found())
config_host_data.set('CONFIG_COCOA', cocoa.found())
config_host_data.set('CONFIG_DARWIN', host_os == 'darwin')
+config_host_data.set('CONFIG_FDT', fdt.found())
config_host_data.set('CONFIG_FUZZ', get_option('fuzzing'))
config_host_data.set('CONFIG_GCOV', get_option('b_coverage'))
config_host_data.set('CONFIG_LIBUDEV', libudev.found())
@@ -2351,7 +2366,6 @@ config_host_data.set('CONFIG_DEBUG_MUTEX', get_option('debug_mutex'))
config_host_data.set('CONFIG_DEBUG_STACK_USAGE', get_option('debug_stack_usage'))
config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg'))
config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap'))
-config_host_data.set('CONFIG_LIVE_BLOCK_MIGRATION', get_option('live_block_migration').allowed())
config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug'))
config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed())
@@ -2961,6 +2975,7 @@ host_kconfig = \
(have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \
(opengl.found() ? ['CONFIG_OPENGL=y'] : []) + \
(x11.found() ? ['CONFIG_X11=y'] : []) + \
+ (fdt.found() ? ['CONFIG_FDT=y'] : []) + \
(have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \
(have_vhost_vdpa ? ['CONFIG_VHOST_VDPA=y'] : []) + \
(have_vhost_kernel ? ['CONFIG_VHOST_KERNEL=y'] : []) + \
@@ -3005,7 +3020,7 @@ foreach target : target_dirs
}
endif
- accel_kconfig = []
+ target_kconfig = []
foreach sym: accelerators
if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, [])
config_target += { sym: 'y' }
@@ -3015,24 +3030,30 @@ foreach target : target_dirs
else
config_target += { 'CONFIG_TCG_BUILTIN': 'y' }
endif
- accel_kconfig += [ sym + '=y' ]
+ target_kconfig += [ sym + '=y' ]
endif
endforeach
- if accel_kconfig.length() == 0
+ if target_kconfig.length() == 0
if default_targets
continue
endif
error('No accelerator available for target @0@'.format(target))
endif
- actual_target_dirs += target
config_target += keyval.load('configs/targets' / target + '.mak')
config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' }
- if 'TARGET_NEED_FDT' in config_target
- fdt_required += target
+ if 'TARGET_NEED_FDT' in config_target and not fdt.found()
+ if default_targets
+ warning('Disabling ' + target + ' due to missing libfdt')
+ else
+ fdt_required += target
+ endif
+ continue
endif
+ actual_target_dirs += target
+
# Add default keys
if 'TARGET_BASE_ARCH' not in config_target
config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']}
@@ -3078,6 +3099,9 @@ foreach target : target_dirs
configuration: config_target_data)}
if target.endswith('-softmmu')
+ target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y'
+ target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN']
+
config_input = meson.get_external_property(target, 'default')
config_devices_mak = target + '-config-devices.mak'
config_devices_mak = configure_file(
@@ -3088,8 +3112,7 @@ foreach target : target_dirs
command: [minikconf,
get_option('default_devices') ? '--defconfig' : '--allnoconfig',
config_devices_mak, '@DEPFILE@', '@INPUT@',
- host_kconfig, accel_kconfig,
- 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y'])
+ host_kconfig, target_kconfig])
config_devices_data = configuration_data()
config_devices = keyval.load(config_devices_mak)
@@ -3118,6 +3141,10 @@ genh += custom_target('config-poison.h',
command: [find_program('scripts/make-config-poison.sh'),
target_configs_h])
+if fdt_required.length() > 0
+ error('fdt disabled but required by targets ' + ', '.join(fdt_required))
+endif
+
###############
# Subprojects #
###############
@@ -3128,44 +3155,6 @@ if have_system and vfio_user_server_allowed
libvfio_user_dep = libvfio_user_proj.get_variable('libvfio_user_dep')
endif
-fdt = not_found
-fdt_opt = get_option('fdt')
-if fdt_required.length() > 0 or fdt_opt == 'enabled'
- if fdt_opt == 'disabled'
- error('fdt disabled but required by targets ' + ', '.join(fdt_required))
- endif
-
- if fdt_opt in ['enabled', 'auto', 'system']
- if get_option('wrap_mode') == 'nodownload'
- fdt_opt = 'system'
- endif
- fdt = cc.find_library('fdt', required: fdt_opt == 'system')
- if fdt.found() and cc.links('''
- #include <libfdt.h>
- #include <libfdt_env.h>
- int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''',
- dependencies: fdt)
- fdt_opt = 'system'
- elif fdt_opt == 'system'
- error('system libfdt requested, but it is too old (1.5.1 or newer required)')
- else
- fdt_opt = 'internal'
- fdt = not_found
- endif
- endif
- if not fdt.found()
- assert(fdt_opt == 'internal')
- libfdt_proj = subproject('dtc', required: true,
- default_options: ['tools=false', 'yaml=disabled',
- 'python=disabled', 'default_library=static'])
- fdt = libfdt_proj.get_variable('libfdt_dep')
- endif
-else
- fdt_opt = 'disabled'
-endif
-
-config_host_data.set('CONFIG_FDT', fdt.found())
-
vhost_user = not_found
if host_os == 'linux' and have_vhost_user
libvhost_user = subproject('libvhost-user')
@@ -3866,15 +3855,23 @@ foreach target : target_dirs
target_common = common_ss.apply(config_target, strict: false)
objects = common_all.extract_objects(target_common.sources())
- deps = target_common.dependencies()
+ arch_deps += target_common.dependencies()
target_specific = specific_ss.apply(config_target, strict: false)
arch_srcs += target_specific.sources()
arch_deps += target_specific.dependencies()
+ # allow using headers from the dependencies but do not include the sources,
+ # because this emulator only needs those in "objects". For external
+ # dependencies, the full dependency is included below in the executable.
+ lib_deps = []
+ foreach dep : arch_deps
+ lib_deps += dep.partial_dependency(compile_args: true, includes: true)
+ endforeach
+
lib = static_library('qemu-' + target,
sources: arch_srcs + genh,
- dependencies: arch_deps,
+ dependencies: lib_deps,
objects: objects,
include_directories: target_inc,
c_args: c_args,
@@ -3922,7 +3919,7 @@ foreach target : target_dirs
emulator = executable(exe_name, exe['sources'],
install: true,
c_args: c_args,
- dependencies: arch_deps + deps + exe['dependencies'],
+ dependencies: arch_deps + exe['dependencies'],
objects: lib.extract_all_objects(recursive: true),
link_depends: [block_syms, qemu_syms],
link_args: link_args,
@@ -4300,7 +4297,6 @@ if have_block
summary_info += {'Use block whitelist in tools': get_option('block_drv_whitelist_in_tools')}
summary_info += {'VirtFS (9P) support': have_virtfs}
summary_info += {'VirtFS (9P) Proxy Helper support (deprecated)': have_virtfs_proxy_helper}
- summary_info += {'Live block migration': config_host_data.get('CONFIG_LIVE_BLOCK_MIGRATION')}
summary_info += {'replication support': config_host_data.get('CONFIG_REPLICATION')}
summary_info += {'bochs support': get_option('bochs').allowed()}
summary_info += {'cloop support': get_option('cloop').allowed()}
@@ -4409,7 +4405,7 @@ summary_info += {'Linux AIO support': libaio}
summary_info += {'Linux io_uring support': linux_io_uring}
summary_info += {'ATTR/XATTR support': libattr}
summary_info += {'RDMA support': rdma}
-summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
+summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
diff --git a/meson_options.txt b/meson_options.txt
index adc77bae0c..4c1583eb40 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -314,8 +314,6 @@ option('fdt', type: 'combo', value: 'auto',
option('selinux', type: 'feature', value: 'auto',
description: 'SELinux support in qemu-nbd')
-option('live_block_migration', type: 'feature', value: 'auto',
- description: 'block migration in the main migration stream')
option('replication', type: 'feature', value: 'auto',
description: 'replication support')
option('colo_proxy', type: 'feature', value: 'auto',
diff --git a/migration/block.c b/migration/block.c
deleted file mode 100644
index bae6e94891..0000000000
--- a/migration/block.c
+++ /dev/null
@@ -1,1019 +0,0 @@
-/*
- * QEMU live block migration
- *
- * Copyright IBM, Corp. 2009
- *
- * Authors:
- * Liran Schour <lirans@il.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/error-report.h"
-#include "qemu/main-loop.h"
-#include "qemu/cutils.h"
-#include "qemu/queue.h"
-#include "block.h"
-#include "block/dirty-bitmap.h"
-#include "migration/misc.h"
-#include "migration.h"
-#include "migration-stats.h"
-#include "migration/register.h"
-#include "qemu-file.h"
-#include "migration/vmstate.h"
-#include "sysemu/block-backend.h"
-#include "trace.h"
-#include "options.h"
-
-#define BLK_MIG_BLOCK_SIZE (1ULL << 20)
-#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLK_MIG_BLOCK_SIZE >> BDRV_SECTOR_BITS)
-
-#define BLK_MIG_FLAG_DEVICE_BLOCK 0x01
-#define BLK_MIG_FLAG_EOS 0x02
-#define BLK_MIG_FLAG_PROGRESS 0x04
-#define BLK_MIG_FLAG_ZERO_BLOCK 0x08
-
-#define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE)
-
-#define MAX_IO_BUFFERS 512
-#define MAX_PARALLEL_IO 16
-
-typedef struct BlkMigDevState {
- /* Written during setup phase. Can be read without a lock. */
- BlockBackend *blk;
- char *blk_name;
- int shared_base;
- int64_t total_sectors;
- QSIMPLEQ_ENTRY(BlkMigDevState) entry;
- Error *blocker;
-
- /* Only used by migration thread. Does not need a lock. */
- int bulk_completed;
- int64_t cur_sector;
- int64_t cur_dirty;
-
- /* Data in the aio_bitmap is protected by block migration lock.
- * Allocation and free happen during setup and cleanup respectively.
- */
- unsigned long *aio_bitmap;
-
- /* Protected by block migration lock. */
- int64_t completed_sectors;
-
- /* During migration this is protected by bdrv_dirty_bitmap_lock().
- * Allocation and free happen during setup and cleanup respectively.
- */
- BdrvDirtyBitmap *dirty_bitmap;
-} BlkMigDevState;
-
-typedef struct BlkMigBlock {
- /* Only used by migration thread. */
- uint8_t *buf;
- BlkMigDevState *bmds;
- int64_t sector;
- int nr_sectors;
- QEMUIOVector qiov;
- BlockAIOCB *aiocb;
-
- /* Protected by block migration lock. */
- int ret;
- QSIMPLEQ_ENTRY(BlkMigBlock) entry;
-} BlkMigBlock;
-
-typedef struct BlkMigState {
- QSIMPLEQ_HEAD(, BlkMigDevState) bmds_list;
- int64_t total_sector_sum;
- bool zero_blocks;
-
- /* Protected by lock. */
- QSIMPLEQ_HEAD(, BlkMigBlock) blk_list;
- int submitted;
- int read_done;
-
- /* Only used by migration thread. Does not need a lock. */
- int transferred;
- int prev_progress;
- int bulk_completed;
-
- /* Lock must be taken _inside_ the BQL. */
- QemuMutex lock;
-} BlkMigState;
-
-static BlkMigState block_mig_state;
-
-static void blk_mig_lock(void)
-{
- qemu_mutex_lock(&block_mig_state.lock);
-}
-
-static void blk_mig_unlock(void)
-{
- qemu_mutex_unlock(&block_mig_state.lock);
-}
-
-/* Must run outside of the BQL during the bulk phase,
- * or the VM will stall.
- */
-
-static void blk_send(QEMUFile *f, BlkMigBlock * blk)
-{
- int len;
- uint64_t flags = BLK_MIG_FLAG_DEVICE_BLOCK;
-
- if (block_mig_state.zero_blocks &&
- buffer_is_zero(blk->buf, BLK_MIG_BLOCK_SIZE)) {
- flags |= BLK_MIG_FLAG_ZERO_BLOCK;
- }
-
- /* sector number and flags */
- qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS)
- | flags);
-
- /* device name */
- len = strlen(blk->bmds->blk_name);
- qemu_put_byte(f, len);
- qemu_put_buffer(f, (uint8_t *) blk->bmds->blk_name, len);
-
- /* if a block is zero we need to flush here since the network
- * bandwidth is now a lot higher than the storage device bandwidth.
- * thus if we queue zero blocks we slow down the migration */
- if (flags & BLK_MIG_FLAG_ZERO_BLOCK) {
- qemu_fflush(f);
- return;
- }
-
- qemu_put_buffer(f, blk->buf, BLK_MIG_BLOCK_SIZE);
-}
-
-int blk_mig_active(void)
-{
- return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list);
-}
-
-int blk_mig_bulk_active(void)
-{
- return blk_mig_active() && !block_mig_state.bulk_completed;
-}
-
-uint64_t blk_mig_bytes_transferred(void)
-{
- BlkMigDevState *bmds;
- uint64_t sum = 0;
-
- blk_mig_lock();
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- sum += bmds->completed_sectors;
- }
- blk_mig_unlock();
- return sum << BDRV_SECTOR_BITS;
-}
-
-uint64_t blk_mig_bytes_remaining(void)
-{
- return blk_mig_bytes_total() - blk_mig_bytes_transferred();
-}
-
-uint64_t blk_mig_bytes_total(void)
-{
- BlkMigDevState *bmds;
- uint64_t sum = 0;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- sum += bmds->total_sectors;
- }
- return sum << BDRV_SECTOR_BITS;
-}
-
-
-/* Called with migration lock held. */
-
-static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
-{
- int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
-
- if (sector < bmds->total_sectors) {
- return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
- (1UL << (chunk % (sizeof(unsigned long) * 8))));
- } else {
- return 0;
- }
-}
-
-/* Called with migration lock held. */
-
-static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
- int nb_sectors, int set)
-{
- int64_t start, end;
- unsigned long val, idx, bit;
-
- start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
- end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
-
- for (; start <= end; start++) {
- idx = start / (sizeof(unsigned long) * 8);
- bit = start % (sizeof(unsigned long) * 8);
- val = bmds->aio_bitmap[idx];
- if (set) {
- val |= 1UL << bit;
- } else {
- val &= ~(1UL << bit);
- }
- bmds->aio_bitmap[idx] = val;
- }
-}
-
-static void alloc_aio_bitmap(BlkMigDevState *bmds)
-{
- int64_t bitmap_size;
-
- bitmap_size = bmds->total_sectors + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
- bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
-
- bmds->aio_bitmap = g_malloc0(bitmap_size);
-}
-
-/* Never hold migration lock when yielding to the main loop! */
-
-static void blk_mig_read_cb(void *opaque, int ret)
-{
- BlkMigBlock *blk = opaque;
-
- blk_mig_lock();
- blk->ret = ret;
-
- QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
- bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
-
- block_mig_state.submitted--;
- block_mig_state.read_done++;
- assert(block_mig_state.submitted >= 0);
- blk_mig_unlock();
-}
-
-/* Called with no lock taken. */
-
-static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
-{
- int64_t total_sectors = bmds->total_sectors;
- int64_t cur_sector = bmds->cur_sector;
- BlockBackend *bb = bmds->blk;
- BlkMigBlock *blk;
- int nr_sectors;
- int64_t count;
-
- if (bmds->shared_base) {
- bql_lock();
- /* Skip unallocated sectors; intentionally treats failure or
- * partial sector as an allocated sector */
- while (cur_sector < total_sectors &&
- !bdrv_is_allocated(blk_bs(bb), cur_sector * BDRV_SECTOR_SIZE,
- MAX_IS_ALLOCATED_SEARCH, &count)) {
- if (count < BDRV_SECTOR_SIZE) {
- break;
- }
- cur_sector += count >> BDRV_SECTOR_BITS;
- }
- bql_unlock();
- }
-
- if (cur_sector >= total_sectors) {
- bmds->cur_sector = bmds->completed_sectors = total_sectors;
- return 1;
- }
-
- bmds->completed_sectors = cur_sector;
-
- cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1);
-
- /* we are going to transfer a full block even if it is not allocated */
- nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
-
- if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
- nr_sectors = total_sectors - cur_sector;
- }
-
- blk = g_new(BlkMigBlock, 1);
- blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE);
- blk->bmds = bmds;
- blk->sector = cur_sector;
- blk->nr_sectors = nr_sectors;
-
- qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE);
-
- blk_mig_lock();
- block_mig_state.submitted++;
- blk_mig_unlock();
-
- /*
- * The migration thread does not have an AioContext. Lock the BQL so that
- * I/O runs in the main loop AioContext (see
- * qemu_get_current_aio_context()).
- */
- bql_lock();
- bdrv_reset_dirty_bitmap(bmds->dirty_bitmap, cur_sector * BDRV_SECTOR_SIZE,
- nr_sectors * BDRV_SECTOR_SIZE);
- blk->aiocb = blk_aio_preadv(bb, cur_sector * BDRV_SECTOR_SIZE, &blk->qiov,
- 0, blk_mig_read_cb, blk);
- bql_unlock();
-
- bmds->cur_sector = cur_sector + nr_sectors;
- return (bmds->cur_sector >= total_sectors);
-}
-
-/* Called with the BQL taken. */
-
-static int set_dirty_tracking(void)
-{
- BlkMigDevState *bmds;
- int ret;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- bmds->dirty_bitmap = bdrv_create_dirty_bitmap(blk_bs(bmds->blk),
- BLK_MIG_BLOCK_SIZE,
- NULL, NULL);
- if (!bmds->dirty_bitmap) {
- ret = -errno;
- goto fail;
- }
- }
- return 0;
-
-fail:
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- if (bmds->dirty_bitmap) {
- bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
- }
- }
- return ret;
-}
-
-/* Called with the BQL taken. */
-
-static void unset_dirty_tracking(void)
-{
- BlkMigDevState *bmds;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- if (bmds->dirty_bitmap) {
- bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
- }
- }
-}
-
-static int init_blk_migration(QEMUFile *f, Error **errp)
-{
- BlockDriverState *bs;
- BlkMigDevState *bmds;
- int64_t sectors;
- BdrvNextIterator it;
- int i, num_bs = 0;
- struct {
- BlkMigDevState *bmds;
- BlockDriverState *bs;
- } *bmds_bs;
- int ret;
-
- GRAPH_RDLOCK_GUARD_MAINLOOP();
-
- block_mig_state.submitted = 0;
- block_mig_state.read_done = 0;
- block_mig_state.transferred = 0;
- block_mig_state.total_sector_sum = 0;
- block_mig_state.prev_progress = -1;
- block_mig_state.bulk_completed = 0;
- block_mig_state.zero_blocks = migrate_zero_blocks();
-
- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
- num_bs++;
- }
- bmds_bs = g_malloc0(num_bs * sizeof(*bmds_bs));
-
- for (i = 0, bs = bdrv_first(&it); bs; bs = bdrv_next(&it), i++) {
- if (bdrv_is_read_only(bs)) {
- continue;
- }
-
- sectors = bdrv_nb_sectors(bs);
- if (sectors == 0) {
- continue;
- }
- if (sectors < 0) {
- error_setg(errp, "Error getting length of block device %s",
- bdrv_get_device_name(bs));
- ret = sectors;
- bdrv_next_cleanup(&it);
- goto out;
- }
-
- bmds = g_new0(BlkMigDevState, 1);
- bmds->blk = blk_new(qemu_get_aio_context(),
- BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL);
- bmds->blk_name = g_strdup(bdrv_get_device_name(bs));
- bmds->bulk_completed = 0;
- bmds->total_sectors = sectors;
- bmds->completed_sectors = 0;
- bmds->shared_base = migrate_block_incremental();
-
- assert(i < num_bs);
- bmds_bs[i].bmds = bmds;
- bmds_bs[i].bs = bs;
-
- block_mig_state.total_sector_sum += sectors;
-
- if (bmds->shared_base) {
- trace_migration_block_init_shared(bdrv_get_device_name(bs));
- } else {
- trace_migration_block_init_full(bdrv_get_device_name(bs));
- }
-
- QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
- }
-
- /* Can only insert new BDSes now because doing so while iterating block
- * devices may end up in a deadlock (iterating the new BDSes, too). */
- for (i = 0; i < num_bs; i++) {
- bmds = bmds_bs[i].bmds;
- bs = bmds_bs[i].bs;
-
- if (bmds) {
- ret = blk_insert_bs(bmds->blk, bs, errp);
- if (ret < 0) {
- goto out;
- }
-
- alloc_aio_bitmap(bmds);
- error_setg(&bmds->blocker, "block device is in use by migration");
- bdrv_op_block_all(bs, bmds->blocker);
- }
- }
-
- ret = 0;
-out:
- g_free(bmds_bs);
- return ret;
-}
-
-/* Called with no lock taken. */
-
-static int blk_mig_save_bulked_block(QEMUFile *f)
-{
- int64_t completed_sector_sum = 0;
- BlkMigDevState *bmds;
- int progress;
- int ret = 0;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- if (bmds->bulk_completed == 0) {
- if (mig_save_device_bulk(f, bmds) == 1) {
- /* completed bulk section for this device */
- bmds->bulk_completed = 1;
- }
- completed_sector_sum += bmds->completed_sectors;
- ret = 1;
- break;
- } else {
- completed_sector_sum += bmds->completed_sectors;
- }
- }
-
- if (block_mig_state.total_sector_sum != 0) {
- progress = completed_sector_sum * 100 /
- block_mig_state.total_sector_sum;
- } else {
- progress = 100;
- }
- if (progress != block_mig_state.prev_progress) {
- block_mig_state.prev_progress = progress;
- qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
- | BLK_MIG_FLAG_PROGRESS);
- trace_migration_block_progression(progress);
- }
-
- return ret;
-}
-
-static void blk_mig_reset_dirty_cursor(void)
-{
- BlkMigDevState *bmds;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- bmds->cur_dirty = 0;
- }
-}
-
-/* Called with the BQL taken. */
-
-static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
- int is_async)
-{
- BlkMigBlock *blk;
- int64_t total_sectors = bmds->total_sectors;
- int64_t sector;
- int nr_sectors;
- int ret = -EIO;
-
- for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
- blk_mig_lock();
- if (bmds_aio_inflight(bmds, sector)) {
- blk_mig_unlock();
- blk_drain(bmds->blk);
- } else {
- blk_mig_unlock();
- }
- bdrv_dirty_bitmap_lock(bmds->dirty_bitmap);
- if (bdrv_dirty_bitmap_get_locked(bmds->dirty_bitmap,
- sector * BDRV_SECTOR_SIZE)) {
- if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
- nr_sectors = total_sectors - sector;
- } else {
- nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
- }
- bdrv_reset_dirty_bitmap_locked(bmds->dirty_bitmap,
- sector * BDRV_SECTOR_SIZE,
- nr_sectors * BDRV_SECTOR_SIZE);
- bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap);
-
- blk = g_new(BlkMigBlock, 1);
- blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE);
- blk->bmds = bmds;
- blk->sector = sector;
- blk->nr_sectors = nr_sectors;
-
- if (is_async) {
- qemu_iovec_init_buf(&blk->qiov, blk->buf,
- nr_sectors * BDRV_SECTOR_SIZE);
-
- blk->aiocb = blk_aio_preadv(bmds->blk,
- sector * BDRV_SECTOR_SIZE,
- &blk->qiov, 0, blk_mig_read_cb,
- blk);
-
- blk_mig_lock();
- block_mig_state.submitted++;
- bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
- blk_mig_unlock();
- } else {
- ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE,
- nr_sectors * BDRV_SECTOR_SIZE, blk->buf, 0);
- if (ret < 0) {
- goto error;
- }
- blk_send(f, blk);
-
- g_free(blk->buf);
- g_free(blk);
- }
-
- sector += nr_sectors;
- bmds->cur_dirty = sector;
- break;
- }
-
- bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap);
- sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
- bmds->cur_dirty = sector;
- }
-
- return (bmds->cur_dirty >= bmds->total_sectors);
-
-error:
- trace_migration_block_save_device_dirty(sector);
- g_free(blk->buf);
- g_free(blk);
- return ret;
-}
-
-/* Called with the BQL taken.
- *
- * return value:
- * 0: too much data for max_downtime
- * 1: few enough data for max_downtime
-*/
-static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
-{
- BlkMigDevState *bmds;
- int ret = 1;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- ret = mig_save_device_dirty(f, bmds, is_async);
- if (ret <= 0) {
- break;
- }
- }
-
- return ret;
-}
-
-/* Called with no locks taken. */
-
-static int flush_blks(QEMUFile *f)
-{
- BlkMigBlock *blk;
- int ret = 0;
-
- trace_migration_block_flush_blks("Enter", block_mig_state.submitted,
- block_mig_state.read_done,
- block_mig_state.transferred);
-
- blk_mig_lock();
- while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
- if (migration_rate_exceeded(f)) {
- break;
- }
- if (blk->ret < 0) {
- ret = blk->ret;
- break;
- }
-
- QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
- blk_mig_unlock();
- blk_send(f, blk);
- blk_mig_lock();
-
- g_free(blk->buf);
- g_free(blk);
-
- block_mig_state.read_done--;
- block_mig_state.transferred++;
- assert(block_mig_state.read_done >= 0);
- }
- blk_mig_unlock();
-
- trace_migration_block_flush_blks("Exit", block_mig_state.submitted,
- block_mig_state.read_done,
- block_mig_state.transferred);
- return ret;
-}
-
-/* Called with the BQL taken. */
-
-static int64_t get_remaining_dirty(void)
-{
- BlkMigDevState *bmds;
- int64_t dirty = 0;
-
- QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
- bdrv_dirty_bitmap_lock(bmds->dirty_bitmap);
- dirty += bdrv_get_dirty_count(bmds->dirty_bitmap);
- bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap);
- }
-
- return dirty;
-}
-
-
-
-/* Called with the BQL taken. */
-static void block_migration_cleanup_bmds(void)
-{
- BlkMigDevState *bmds;
- BlockDriverState *bs;
-
- unset_dirty_tracking();
-
- while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
- QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
-
- bs = blk_bs(bmds->blk);
- if (bs) {
- bdrv_op_unblock_all(bs, bmds->blocker);
- }
- error_free(bmds->blocker);
- blk_unref(bmds->blk);
- g_free(bmds->blk_name);
- g_free(bmds->aio_bitmap);
- g_free(bmds);
- }
-}
-
-/* Called with the BQL taken. */
-static void block_migration_cleanup(void *opaque)
-{
- BlkMigBlock *blk;
-
- bdrv_drain_all();
-
- block_migration_cleanup_bmds();
-
- blk_mig_lock();
- while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
- QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
- g_free(blk->buf);
- g_free(blk);
- }
- blk_mig_unlock();
-}
-
-static int block_save_setup(QEMUFile *f, void *opaque, Error **errp)
-{
- int ret;
-
- trace_migration_block_save("setup", block_mig_state.submitted,
- block_mig_state.transferred);
-
- warn_report("block migration is deprecated;"
- " use blockdev-mirror with NBD instead");
-
- ret = init_blk_migration(f, errp);
- if (ret < 0) {
- return ret;
- }
-
- /* start track dirty blocks */
- ret = set_dirty_tracking();
- if (ret) {
- error_setg_errno(errp, -ret, "Failed to start block dirty tracking");
- return ret;
- }
-
- ret = flush_blks(f);
- if (ret) {
- error_setg_errno(errp, -ret, "Flushing block failed");
- return ret;
- }
- blk_mig_reset_dirty_cursor();
- qemu_put_be64(f, BLK_MIG_FLAG_EOS);
-
- return ret;
-}
-
-static int block_save_iterate(QEMUFile *f, void *opaque)
-{
- int ret;
- uint64_t last_bytes = qemu_file_transferred(f);
-
- trace_migration_block_save("iterate", block_mig_state.submitted,
- block_mig_state.transferred);
-
- ret = flush_blks(f);
- if (ret) {
- return ret;
- }
-
- blk_mig_reset_dirty_cursor();
-
- /* control the rate of transfer */
- blk_mig_lock();
- while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE <
- migration_rate_get() &&
- block_mig_state.submitted < MAX_PARALLEL_IO &&
- (block_mig_state.submitted + block_mig_state.read_done) <
- MAX_IO_BUFFERS) {
- blk_mig_unlock();
- if (block_mig_state.bulk_completed == 0) {
- /* first finish the bulk phase */
- if (blk_mig_save_bulked_block(f) == 0) {
- /* finished saving bulk on all devices */
- block_mig_state.bulk_completed = 1;
- }
- ret = 0;
- } else {
- /* Always called with the BQL taken for
- * simplicity, block_save_complete also calls it.
- */
- bql_lock();
- ret = blk_mig_save_dirty_block(f, 1);
- bql_unlock();
- }
- if (ret < 0) {
- return ret;
- }
- blk_mig_lock();
- if (ret != 0) {
- /* no more dirty blocks */
- break;
- }
- }
- blk_mig_unlock();
-
- ret = flush_blks(f);
- if (ret) {
- return ret;
- }
-
- qemu_put_be64(f, BLK_MIG_FLAG_EOS);
- uint64_t delta_bytes = qemu_file_transferred(f) - last_bytes;
- return (delta_bytes > 0);
-}
-
-/* Called with the BQL taken. */
-
-static int block_save_complete(QEMUFile *f, void *opaque)
-{
- int ret;
-
- trace_migration_block_save("complete", block_mig_state.submitted,
- block_mig_state.transferred);
-
- ret = flush_blks(f);
- if (ret) {
- return ret;
- }
-
- blk_mig_reset_dirty_cursor();
-
- /* we know for sure that save bulk is completed and
- all async read completed */
- blk_mig_lock();
- assert(block_mig_state.submitted == 0);
- blk_mig_unlock();
-
- do {
- ret = blk_mig_save_dirty_block(f, 0);
- if (ret < 0) {
- return ret;
- }
- } while (ret == 0);
-
- /* report completion */
- qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
-
- trace_migration_block_save_complete();
-
- qemu_put_be64(f, BLK_MIG_FLAG_EOS);
-
- /* Make sure that our BlockBackends are gone, so that the block driver
- * nodes can be inactivated. */
- block_migration_cleanup_bmds();
-
- return 0;
-}
-
-static void block_state_pending(void *opaque, uint64_t *must_precopy,
- uint64_t *can_postcopy)
-{
- /* Estimate pending number of bytes to send */
- uint64_t pending;
-
- bql_lock();
- pending = get_remaining_dirty();
- bql_unlock();
-
- blk_mig_lock();
- pending += block_mig_state.submitted * BLK_MIG_BLOCK_SIZE +
- block_mig_state.read_done * BLK_MIG_BLOCK_SIZE;
- blk_mig_unlock();
-
- /* Report at least one block pending during bulk phase */
- if (!pending && !block_mig_state.bulk_completed) {
- pending = BLK_MIG_BLOCK_SIZE;
- }
-
- trace_migration_block_state_pending(pending);
- /* We don't do postcopy */
- *must_precopy += pending;
-}
-
-static int block_load(QEMUFile *f, void *opaque, int version_id)
-{
- static int banner_printed;
- int len, flags;
- char device_name[256];
- int64_t addr;
- BlockBackend *blk, *blk_prev = NULL;
- Error *local_err = NULL;
- uint8_t *buf;
- int64_t total_sectors = 0;
- int nr_sectors;
- int ret;
- BlockDriverInfo bdi;
- int cluster_size = BLK_MIG_BLOCK_SIZE;
-
- do {
- addr = qemu_get_be64(f);
-
- flags = addr & (BDRV_SECTOR_SIZE - 1);
- addr >>= BDRV_SECTOR_BITS;
-
- if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) {
- /* get device name */
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)device_name, len);
- device_name[len] = '\0';
-
- blk = blk_by_name(device_name);
- if (!blk) {
- fprintf(stderr, "Error unknown block device %s\n",
- device_name);
- return -EINVAL;
- }
-
- if (blk != blk_prev) {
- blk_prev = blk;
- total_sectors = blk_nb_sectors(blk);
- if (total_sectors <= 0) {
- error_report("Error getting length of block device %s",
- device_name);
- return -EINVAL;
- }
-
- blk_activate(blk, &local_err);
- if (local_err) {
- error_report_err(local_err);
- return -EINVAL;
- }
-
- ret = bdrv_get_info(blk_bs(blk), &bdi);
- if (ret == 0 && bdi.cluster_size > 0 &&
- bdi.cluster_size <= BLK_MIG_BLOCK_SIZE &&
- BLK_MIG_BLOCK_SIZE % bdi.cluster_size == 0) {
- cluster_size = bdi.cluster_size;
- } else {
- cluster_size = BLK_MIG_BLOCK_SIZE;
- }
- }
-
- if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) {
- nr_sectors = total_sectors - addr;
- } else {
- nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
- }
-
- if (flags & BLK_MIG_FLAG_ZERO_BLOCK) {
- ret = blk_pwrite_zeroes(blk, addr * BDRV_SECTOR_SIZE,
- nr_sectors * BDRV_SECTOR_SIZE,
- BDRV_REQ_MAY_UNMAP);
- } else {
- int i;
- int64_t cur_addr;
- uint8_t *cur_buf;
-
- buf = g_malloc(BLK_MIG_BLOCK_SIZE);
- qemu_get_buffer(f, buf, BLK_MIG_BLOCK_SIZE);
- for (i = 0; i < BLK_MIG_BLOCK_SIZE / cluster_size; i++) {
- cur_addr = addr * BDRV_SECTOR_SIZE + i * cluster_size;
- cur_buf = buf + i * cluster_size;
-
- if ((!block_mig_state.zero_blocks ||
- cluster_size < BLK_MIG_BLOCK_SIZE) &&
- buffer_is_zero(cur_buf, cluster_size)) {
- ret = blk_pwrite_zeroes(blk, cur_addr,
- cluster_size,
- BDRV_REQ_MAY_UNMAP);
- } else {
- ret = blk_pwrite(blk, cur_addr, cluster_size, cur_buf,
- 0);
- }
- if (ret < 0) {
- break;
- }
- }
- g_free(buf);
- }
-
- if (ret < 0) {
- return ret;
- }
- } else if (flags & BLK_MIG_FLAG_PROGRESS) {
- if (!banner_printed) {
- printf("Receiving block device images\n");
- banner_printed = 1;
- }
- printf("Completed %d %%%c", (int)addr,
- (addr == 100) ? '\n' : '\r');
- fflush(stdout);
- } else if (!(flags & BLK_MIG_FLAG_EOS)) {
- fprintf(stderr, "Unknown block migration flags: 0x%x\n", flags);
- return -EINVAL;
- }
- ret = qemu_file_get_error(f);
- if (ret != 0) {
- return ret;
- }
- } while (!(flags & BLK_MIG_FLAG_EOS));
-
- return 0;
-}
-
-static bool block_is_active(void *opaque)
-{
- return migrate_block();
-}
-
-static SaveVMHandlers savevm_block_handlers = {
- .save_setup = block_save_setup,
- .save_live_iterate = block_save_iterate,
- .save_live_complete_precopy = block_save_complete,
- .state_pending_exact = block_state_pending,
- .state_pending_estimate = block_state_pending,
- .load_state = block_load,
- .save_cleanup = block_migration_cleanup,
- .is_active = block_is_active,
-};
-
-void blk_mig_init(void)
-{
- QSIMPLEQ_INIT(&block_mig_state.bmds_list);
- QSIMPLEQ_INIT(&block_mig_state.blk_list);
- qemu_mutex_init(&block_mig_state.lock);
-
- register_savevm_live("block", 0, 1, &savevm_block_handlers,
- &block_mig_state);
-}
diff --git a/migration/block.h b/migration/block.h
deleted file mode 100644
index 3178609dbd..0000000000
--- a/migration/block.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * QEMU live block migration
- *
- * Copyright IBM, Corp. 2009
- *
- * Authors:
- * Liran Schour <lirans@il.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- */
-
-#ifndef MIGRATION_BLOCK_H
-#define MIGRATION_BLOCK_H
-
-#ifdef CONFIG_LIVE_BLOCK_MIGRATION
-int blk_mig_active(void);
-int blk_mig_bulk_active(void);
-uint64_t blk_mig_bytes_transferred(void);
-uint64_t blk_mig_bytes_remaining(void);
-uint64_t blk_mig_bytes_total(void);
-
-#else
-static inline int blk_mig_active(void)
-{
- return false;
-}
-
-static inline int blk_mig_bulk_active(void)
-{
- return false;
-}
-
-static inline uint64_t blk_mig_bytes_transferred(void)
-{
- return 0;
-}
-
-static inline uint64_t blk_mig_bytes_remaining(void)
-{
- return 0;
-}
-
-static inline uint64_t blk_mig_bytes_total(void)
-{
- return 0;
-}
-#endif /* CONFIG_LIVE_BLOCK_MIGRATION */
-
-void migrate_set_block_enabled(bool value, Error **errp);
-#endif /* MIGRATION_BLOCK_H */
diff --git a/migration/colo-stubs.c b/migration/colo-stubs.c
index f8c069b739..e22ce65234 100644
--- a/migration/colo-stubs.c
+++ b/migration/colo-stubs.c
@@ -9,9 +9,8 @@ void colo_shutdown(void)
{
}
-int coroutine_fn colo_incoming_co(void)
+void coroutine_fn colo_incoming_co(void)
{
- return 0;
}
void colo_checkpoint_delay_set(void)
diff --git a/migration/colo.c b/migration/colo.c
index 5600a43d78..f96c2ee069 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -18,7 +18,6 @@
#include "qemu-file.h"
#include "savevm.h"
#include "migration/colo.h"
-#include "block.h"
#include "io/channel-buffer.h"
#include "trace.h"
#include "qemu/error-report.h"
@@ -838,12 +837,11 @@ static void *colo_process_incoming_thread(void *opaque)
/* Make sure all file formats throw away their mutable metadata */
bql_lock();
bdrv_activate_all(&local_err);
+ bql_unlock();
if (local_err) {
- bql_unlock();
error_report_err(local_err);
return NULL;
}
- bql_unlock();
failover_init_state();
@@ -929,16 +927,13 @@ out:
return NULL;
}
-int coroutine_fn colo_incoming_co(void)
+void coroutine_fn colo_incoming_co(void)
{
MigrationIncomingState *mis = migration_incoming_get_current();
QemuThread th;
assert(bql_locked());
-
- if (!migration_incoming_colo_enabled()) {
- return 0;
- }
+ assert(migration_incoming_colo_enabled());
qemu_thread_create(&th, "COLO incoming", colo_process_incoming_thread,
mis, QEMU_THREAD_JOINABLE);
@@ -954,6 +949,4 @@ int coroutine_fn colo_incoming_co(void)
/* We hold the global BQL, so it is safe here */
colo_release_ram_cache();
-
- return 0;
}
diff --git a/migration/fd.c b/migration/fd.c
index 449adaa2de..aab5189eac 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -20,6 +20,8 @@
#include "file.h"
#include "migration.h"
#include "monitor/monitor.h"
+#include "qemu/error-report.h"
+#include "qemu/sockets.h"
#include "io/channel-util.h"
#include "trace.h"
@@ -32,6 +34,11 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **
return;
}
+ if (!fd_is_socket(fd)) {
+ warn_report("fd: migration to a file is deprecated."
+ " Use file: instead.");
+ }
+
trace_migration_fd_outgoing(fd);
ioc = qio_channel_new_fd(fd, errp);
if (!ioc) {
@@ -61,6 +68,11 @@ void fd_start_incoming_migration(const char *fdname, Error **errp)
return;
}
+ if (!fd_is_socket(fd)) {
+ warn_report("fd: migration to a file is deprecated."
+ " Use file: instead.");
+ }
+
trace_migration_fd_incoming(fd);
ioc = qio_channel_new_fd(fd, errp);
diff --git a/migration/meson.build b/migration/meson.build
index f76b1ba328..8815f80837 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -23,7 +23,6 @@ system_ss.add(files(
'multifd.c',
'multifd-zlib.c',
'multifd-zero-page.c',
- 'ram-compress.c',
'options.c',
'postcopy-ram.c',
'savevm.c',
@@ -39,9 +38,6 @@ else
endif
system_ss.add(when: rdma, if_true: files('rdma.c'))
-if get_option('live_block_migration').allowed()
- system_ss.add(files('block.c'))
-endif
system_ss.add(when: zstd, if_true: files('multifd-zstd.c'))
specific_ss.add(when: 'CONFIG_SYSTEM_ONLY',
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 7e96ae6ffd..9f0e8029e0 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -46,8 +46,6 @@ static void migration_global_dump(Monitor *mon)
ms->send_configuration ? "on" : "off");
monitor_printf(mon, "send-section-footer: %s\n",
ms->send_section_footer ? "on" : "off");
- monitor_printf(mon, "decompress-error-check: %s\n",
- ms->decompress_error_check ? "on" : "off");
monitor_printf(mon, "clear-bitmap-shift: %u\n",
ms->clear_bitmap_shift);
}
@@ -105,8 +103,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
info->ram->total >> 10);
monitor_printf(mon, "duplicate: %" PRIu64 " pages\n",
info->ram->duplicate);
- monitor_printf(mon, "skipped: %" PRIu64 " pages\n",
- info->ram->skipped);
monitor_printf(mon, "normal: %" PRIu64 " pages\n",
info->ram->normal);
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
@@ -147,15 +143,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
}
}
- if (info->disk) {
- monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n",
- info->disk->transferred >> 10);
- monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n",
- info->disk->remaining >> 10);
- monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n",
- info->disk->total >> 10);
- }
-
if (info->xbzrle_cache) {
monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
info->xbzrle_cache->cache_size);
@@ -173,19 +160,6 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
info->xbzrle_cache->overflow);
}
- if (info->compression) {
- monitor_printf(mon, "compression pages: %" PRIu64 " pages\n",
- info->compression->pages);
- monitor_printf(mon, "compression busy: %" PRIu64 "\n",
- info->compression->busy);
- monitor_printf(mon, "compression busy rate: %0.2f\n",
- info->compression->busy_rate);
- monitor_printf(mon, "compressed size: %" PRIu64 " kbytes\n",
- info->compression->compressed_size >> 10);
- monitor_printf(mon, "compression rate: %0.2f\n",
- info->compression->compression_rate);
- }
-
if (info->has_cpu_throttle_percentage) {
monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n",
info->cpu_throttle_percentage);
@@ -274,22 +248,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%s: %" PRIu64 " ms\n",
MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP),
params->announce_step);
- assert(params->has_compress_level);
- monitor_printf(mon, "%s: %u\n",
- MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL),
- params->compress_level);
- assert(params->has_compress_threads);
- monitor_printf(mon, "%s: %u\n",
- MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS),
- params->compress_threads);
- assert(params->has_compress_wait_thread);
- monitor_printf(mon, "%s: %s\n",
- MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD),
- params->compress_wait_thread ? "on" : "off");
- assert(params->has_decompress_threads);
- monitor_printf(mon, "%s: %u\n",
- MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS),
- params->decompress_threads);
assert(params->has_throttle_trigger_threshold);
monitor_printf(mon, "%s: %u\n",
MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD),
@@ -334,10 +292,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%s: %u ms\n",
MigrationParameter_str(MIGRATION_PARAMETER_X_CHECKPOINT_DELAY),
params->x_checkpoint_delay);
- assert(params->has_block_incremental);
- monitor_printf(mon, "%s: %s\n",
- MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL),
- params->block_incremental ? "on" : "off");
monitor_printf(mon, "%s: %u\n",
MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS),
params->multifd_channels);
@@ -466,7 +420,7 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict)
}
QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
- qmp_migrate_incoming(NULL, true, caps, &err);
+ qmp_migrate_incoming(NULL, true, caps, true, false, &err);
qapi_free_MigrationChannelList(caps);
end:
@@ -535,22 +489,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
}
switch (val) {
- case MIGRATION_PARAMETER_COMPRESS_LEVEL:
- p->has_compress_level = true;
- visit_type_uint8(v, param, &p->compress_level, &err);
- break;
- case MIGRATION_PARAMETER_COMPRESS_THREADS:
- p->has_compress_threads = true;
- visit_type_uint8(v, param, &p->compress_threads, &err);
- break;
- case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD:
- p->has_compress_wait_thread = true;
- visit_type_bool(v, param, &p->compress_wait_thread, &err);
- break;
- case MIGRATION_PARAMETER_DECOMPRESS_THREADS:
- p->has_decompress_threads = true;
- visit_type_uint8(v, param, &p->decompress_threads, &err);
- break;
case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD:
p->has_throttle_trigger_threshold = true;
visit_type_uint8(v, param, &p->throttle_trigger_threshold, &err);
@@ -618,10 +556,6 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
p->has_x_checkpoint_delay = true;
visit_type_uint32(v, param, &p->x_checkpoint_delay, &err);
break;
- case MIGRATION_PARAMETER_BLOCK_INCREMENTAL:
- p->has_block_incremental = true;
- visit_type_bool(v, param, &p->block_incremental, &err);
- break;
case MIGRATION_PARAMETER_MULTIFD_CHANNELS:
p->has_multifd_channels = true;
visit_type_uint8(v, param, &p->multifd_channels, &err);
@@ -736,24 +670,8 @@ static void hmp_migrate_status_cb(void *opaque)
info = qmp_query_migrate(NULL);
if (!info->has_status || info->status == MIGRATION_STATUS_ACTIVE ||
info->status == MIGRATION_STATUS_SETUP) {
- if (info->disk) {
- int progress;
-
- if (info->disk->remaining) {
- progress = info->disk->transferred * 100 / info->disk->total;
- } else {
- progress = 100;
- }
-
- monitor_printf(status->mon, "Completed %d %%\r", progress);
- monitor_flush(status->mon);
- }
-
timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
} else {
- if (migrate_block()) {
- monitor_printf(status->mon, "\n");
- }
if (info->error_desc) {
error_report("%s", info->error_desc);
}
@@ -768,32 +686,19 @@ static void hmp_migrate_status_cb(void *opaque)
void hmp_migrate(Monitor *mon, const QDict *qdict)
{
bool detach = qdict_get_try_bool(qdict, "detach", false);
- bool blk = qdict_get_try_bool(qdict, "blk", false);
- bool inc = qdict_get_try_bool(qdict, "inc", false);
bool resume = qdict_get_try_bool(qdict, "resume", false);
const char *uri = qdict_get_str(qdict, "uri");
Error *err = NULL;
g_autoptr(MigrationChannelList) caps = NULL;
g_autoptr(MigrationChannel) channel = NULL;
- if (inc) {
- warn_report("option '-i' is deprecated;"
- " use blockdev-mirror with NBD instead");
- }
-
- if (blk) {
- warn_report("option '-b' is deprecated;"
- " use blockdev-mirror with NBD instead");
- }
-
if (!migrate_uri_parse(uri, &channel, &err)) {
hmp_handle_error(mon, err);
return;
}
QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel));
- qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc,
- false, false, true, resume, &err);
+ qmp_migrate(NULL, true, caps, false, false, true, resume, &err);
if (hmp_handle_error(mon, err)) {
return;
}
diff --git a/migration/migration.c b/migration/migration.c
index b5af6b5105..e1b269624c 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -27,7 +27,6 @@
#include "sysemu/cpu-throttle.h"
#include "rdma.h"
#include "ram.h"
-#include "ram-compress.h"
#include "migration/global_state.h"
#include "migration/misc.h"
#include "migration.h"
@@ -46,7 +45,6 @@
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qnull.h"
#include "qemu/rcu.h"
-#include "block.h"
#include "postcopy-ram.h"
#include "qemu/thread.h"
#include "trace.h"
@@ -72,6 +70,8 @@
#define NOTIFIER_ELEM_INIT(array, elem) \
[elem] = NOTIFIER_WITH_RETURN_LIST_INITIALIZER((array)[elem])
+#define INMIGRATE_DEFAULT_EXIT_ON_ERROR true
+
static NotifierWithReturnList migration_state_notifiers[] = {
NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_NORMAL),
NOTIFIER_ELEM_INIT(migration_state_notifiers, MIG_MODE_CPR_REBOOT),
@@ -234,9 +234,10 @@ void migration_object_init(void)
qemu_cond_init(&current_incoming->page_request_cond);
current_incoming->page_requested = g_tree_new(page_request_addr_cmp);
+ current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR;
+
migration_object_check(current_migration, &error_fatal);
- blk_mig_init();
ram_mig_init();
dirty_bitmap_mig_init();
}
@@ -354,7 +355,6 @@ void migration_incoming_state_destroy(void)
struct MigrationIncomingState *mis = migration_incoming_get_current();
multifd_recv_cleanup();
- compress_threads_load_cleanup();
if (mis->to_src_file) {
/* Tell source that we are done */
@@ -513,13 +513,13 @@ void migration_incoming_disable_colo(void)
int migration_incoming_enable_colo(void)
{
#ifndef CONFIG_REPLICATION
- error_report("ENABLE_COLO command come in migration stream, but COLO "
- "module is not built in");
+ error_report("ENABLE_COLO command come in migration stream, but the "
+ "replication module is not built in");
return -ENOTSUP;
#endif
if (!migrate_colo()) {
- error_report("ENABLE_COLO command come in migration stream, but c-colo "
+ error_report("ENABLE_COLO command come in migration stream, but x-colo "
"capability is not set");
return -EINVAL;
}
@@ -647,10 +647,6 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels,
}
#ifdef CONFIG_RDMA
} else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) {
- if (migrate_compress()) {
- error_setg(errp, "RDMA and compression can't be used together");
- return;
- }
if (migrate_xbzrle()) {
error_setg(errp, "RDMA and XBZRLE can't be used together");
return;
@@ -735,17 +731,14 @@ static void process_incoming_migration_bh(void *opaque)
static void coroutine_fn
process_incoming_migration_co(void *opaque)
{
+ MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
PostcopyState ps;
int ret;
+ Error *local_err = NULL;
assert(mis->from_src_file);
- if (compress_threads_load_setup(mis->from_src_file)) {
- error_report("Failed to setup decompress threads");
- goto fail;
- }
-
mis->largest_page_size = qemu_ram_pagesize_largest();
postcopy_state_set(POSTCOPY_INCOMING_NONE);
migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP,
@@ -779,19 +772,13 @@ process_incoming_migration_co(void *opaque)
}
if (ret < 0) {
- MigrationState *s = migrate_get_current();
-
- if (migrate_has_error(s)) {
- WITH_QEMU_LOCK_GUARD(&s->error_mutex) {
- error_report_err(s->error);
- }
- }
- error_report("load of migration failed: %s", strerror(-ret));
+ error_setg(&local_err, "load of migration failed: %s", strerror(-ret));
goto fail;
}
- if (colo_incoming_co() < 0) {
- goto fail;
+ if (migration_incoming_colo_enabled()) {
+ /* yield until COLO exit */
+ colo_incoming_co();
}
migration_bh_schedule(process_incoming_migration_bh, mis);
@@ -799,12 +786,19 @@ process_incoming_migration_co(void *opaque)
fail:
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_FAILED);
- qemu_fclose(mis->from_src_file);
+ migrate_set_error(s, local_err);
+ error_free(local_err);
- multifd_recv_cleanup();
- compress_threads_load_cleanup();
+ migration_incoming_state_destroy();
+
+ if (mis->exit_on_error) {
+ WITH_QEMU_LOCK_GUARD(&s->error_mutex) {
+ error_report_err(s->error);
+ s->error = NULL;
+ }
- exit(EXIT_FAILURE);
+ exit(EXIT_FAILURE);
+ }
}
/**
@@ -1149,8 +1143,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
info->ram->transferred = migration_transferred_bytes();
info->ram->total = ram_bytes_total();
info->ram->duplicate = stat64_get(&mig_stats.zero_pages);
- /* legacy value. It is not used anymore */
- info->ram->skipped = 0;
info->ram->normal = stat64_get(&mig_stats.normal_pages);
info->ram->normal_bytes = info->ram->normal * page_size;
info->ram->mbps = s->mbps;
@@ -1178,8 +1170,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
info->xbzrle_cache->overflow = xbzrle_counters.overflow;
}
- populate_compress(info);
-
if (cpu_throttle_active()) {
info->has_cpu_throttle_percentage = true;
info->cpu_throttle_percentage = cpu_throttle_get_percentage();
@@ -1201,16 +1191,6 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
}
}
-static void populate_disk_info(MigrationInfo *info)
-{
- if (blk_mig_active()) {
- info->disk = g_malloc0(sizeof(*info->disk));
- info->disk->transferred = blk_mig_bytes_transferred();
- info->disk->remaining = blk_mig_bytes_remaining();
- info->disk->total = blk_mig_bytes_total();
- }
-}
-
static void fill_source_migration_info(MigrationInfo *info)
{
MigrationState *s = migrate_get_current();
@@ -1253,7 +1233,6 @@ static void fill_source_migration_info(MigrationInfo *info)
/* TODO add some postcopy stats */
populate_time_info(info, s);
populate_ram_info(info, s);
- populate_disk_info(info);
migration_populate_vfio_info(info);
break;
case MIGRATION_STATUS_COLO:
@@ -1313,6 +1292,15 @@ static void fill_destination_migration_info(MigrationInfo *info)
break;
}
info->status = mis->state;
+
+ if (!info->error_desc) {
+ MigrationState *s = migrate_get_current();
+ QEMU_LOCK_GUARD(&s->error_mutex);
+
+ if (s->error) {
+ info->error_desc = g_strdup(error_get_pretty(s->error));
+ }
+ }
}
MigrationInfo *qmp_query_migrate(Error **errp)
@@ -1409,7 +1397,6 @@ static void migrate_fd_cleanup(MigrationState *s)
type = migration_has_failed(s) ? MIG_EVENT_PRECOPY_FAILED :
MIG_EVENT_PRECOPY_DONE;
migration_call_notifiers(s, type, NULL);
- block_cleanup_parameters();
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
}
@@ -1421,6 +1408,9 @@ static void migrate_fd_cleanup_bh(void *opaque)
void migrate_set_error(MigrationState *s, const Error *error)
{
QEMU_LOCK_GUARD(&s->error_mutex);
+
+ trace_migrate_error(error_get_pretty(error));
+
if (!s->error) {
s->error = error_copy(error);
}
@@ -1444,7 +1434,6 @@ static void migrate_error_free(MigrationState *s)
static void migrate_fd_error(MigrationState *s, const Error *error)
{
- trace_migrate_fd_error(error_get_pretty(error));
assert(s->to_dst_file == NULL);
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_FAILED);
@@ -1794,10 +1783,13 @@ void migrate_del_blocker(Error **reasonp)
}
void qmp_migrate_incoming(const char *uri, bool has_channels,
- MigrationChannelList *channels, Error **errp)
+ MigrationChannelList *channels,
+ bool has_exit_on_error, bool exit_on_error,
+ Error **errp)
{
Error *local_err = NULL;
static bool once = true;
+ MigrationIncomingState *mis = migration_incoming_get_current();
if (!once) {
error_setg(errp, "The incoming migration has already been started");
@@ -1812,6 +1804,9 @@ void qmp_migrate_incoming(const char *uri, bool has_channels,
return;
}
+ mis->exit_on_error =
+ has_exit_on_error ? exit_on_error : INMIGRATE_DEFAULT_EXIT_ON_ERROR;
+
qemu_start_incoming_migration(uri, has_channels, channels, &local_err);
if (local_err) {
@@ -1913,19 +1908,8 @@ bool migration_is_blocked(Error **errp)
}
/* Returns true if continue to migrate, or false if error detected */
-static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
- bool resume, Error **errp)
+static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
{
- if (blk_inc) {
- warn_report("parameter 'inc' is deprecated;"
- " use blockdev-mirror with NBD instead");
- }
-
- if (blk) {
- warn_report("parameter 'blk' is deprecated;"
- " use blockdev-mirror with NBD instead");
- }
-
if (resume) {
if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) {
error_setg(errp, "Cannot resume if there is no "
@@ -2010,26 +1994,6 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
}
}
- if (blk || blk_inc) {
- if (migrate_colo()) {
- error_setg(errp, "No disk migration is required in COLO mode");
- return false;
- }
- if (migrate_block() || migrate_block_incremental()) {
- error_setg(errp, "Command options are incompatible with "
- "current migration capabilities");
- return false;
- }
- if (!migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, true, errp)) {
- return false;
- }
- s->must_remove_block_options = true;
- }
-
- if (blk_inc) {
- migrate_set_block_incremental(true);
- }
-
if (migrate_init(s, errp)) {
return false;
}
@@ -2038,8 +2002,7 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc,
}
void qmp_migrate(const char *uri, bool has_channels,
- MigrationChannelList *channels, bool has_blk, bool blk,
- bool has_inc, bool inc, bool has_detach, bool detach,
+ MigrationChannelList *channels, bool has_detach, bool detach,
bool has_resume, bool resume, Error **errp)
{
bool resume_requested;
@@ -2079,8 +2042,7 @@ void qmp_migrate(const char *uri, bool has_channels,
}
resume_requested = has_resume && resume;
- if (!migrate_prepare(s, has_blk && blk, has_inc && inc,
- resume_requested, errp)) {
+ if (!migrate_prepare(s, resume_requested, errp)) {
/* Error detected, put into errp */
return;
}
@@ -2113,7 +2075,6 @@ void qmp_migrate(const char *uri, bool has_channels,
"a valid migration protocol");
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_FAILED);
- block_cleanup_parameters();
}
if (local_err) {
@@ -3033,13 +2994,15 @@ static MigThrError postcopy_pause(MigrationState *s)
}
}
-void migration_file_set_error(int err)
+void migration_file_set_error(int ret, Error *err)
{
MigrationState *s = current_migration;
WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) {
if (s->to_dst_file) {
- qemu_file_set_error(s->to_dst_file, err);
+ qemu_file_set_error_obj(s->to_dst_file, ret, err);
+ } else if (err) {
+ error_report_err(err);
}
}
}
diff --git a/migration/migration.h b/migration/migration.h
index 8045e39c26..6af01362d4 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -19,7 +19,7 @@
#include "qapi/qapi-types-migration.h"
#include "qapi/qmp/json-writer.h"
#include "qemu/thread.h"
-#include "qemu/coroutine_int.h"
+#include "qemu/coroutine.h"
#include "io/channel.h"
#include "io/channel-buffer.h"
#include "net/announce.h"
@@ -227,6 +227,9 @@ struct MigrationIncomingState {
* is needed as this field is updated serially.
*/
unsigned int switchover_ack_pending_num;
+
+ /* Do exit on incoming migration failure */
+ bool exit_on_error;
};
MigrationIncomingState *migration_incoming_get_current(void);
@@ -376,10 +379,6 @@ struct MigrationState {
/* mutex to protect errp */
QemuMutex error_mutex;
- /* Do we have to clean up -b/-i from old migrate parameters */
- /* This feature is deprecated and will be removed */
- bool must_remove_block_options;
-
/*
* Global switch on whether we need to store the global state
* during migration.
@@ -394,13 +393,6 @@ struct MigrationState {
/* Needed by postcopy-pause state */
QemuSemaphore postcopy_pause_sem;
/*
- * Whether we abort the migration if decompression errors are
- * detected at the destination. It is left at false for qemu
- * older than 3.0, since only newer qemu sends streams that
- * do not trigger spurious decompression errors.
- */
- bool decompress_error_check;
- /*
* This variable only affects behavior when postcopy preempt mode is
* enabled.
*
diff --git a/migration/options.c b/migration/options.c
index 239f5ecfb4..5ab5b6d85d 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -40,13 +40,6 @@
* for sending the last part */
#define DEFAULT_MIGRATE_SET_DOWNTIME 300
-/* Default compression thread count */
-#define DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT 8
-/* Default decompression thread count, usually decompression is at
- * least 4 times as fast as compression.*/
-#define DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT 2
-/*0: means nocompress, 1: best speed, ... 9: best compress ratio */
-#define DEFAULT_MIGRATE_COMPRESS_LEVEL 1
/* Define default autoconverge cpu throttle migration parameters */
#define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50
#define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20
@@ -92,8 +85,6 @@ Property migration_properties[] = {
send_configuration, true),
DEFINE_PROP_BOOL("send-section-footer", MigrationState,
send_section_footer, true),
- DEFINE_PROP_BOOL("decompress-error-check", MigrationState,
- decompress_error_check, true),
DEFINE_PROP_BOOL("multifd-flush-after-each-section", MigrationState,
multifd_flush_after_each_section, false),
DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState,
@@ -102,17 +93,6 @@ Property migration_properties[] = {
preempt_pre_7_2, false),
/* Migration parameters */
- DEFINE_PROP_UINT8("x-compress-level", MigrationState,
- parameters.compress_level,
- DEFAULT_MIGRATE_COMPRESS_LEVEL),
- DEFINE_PROP_UINT8("x-compress-threads", MigrationState,
- parameters.compress_threads,
- DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT),
- DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState,
- parameters.compress_wait_thread, true),
- DEFINE_PROP_UINT8("x-decompress-threads", MigrationState,
- parameters.decompress_threads,
- DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT),
DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState,
parameters.throttle_trigger_threshold,
DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD),
@@ -188,14 +168,12 @@ Property migration_properties[] = {
DEFINE_PROP_MIG_CAP("x-rdma-pin-all", MIGRATION_CAPABILITY_RDMA_PIN_ALL),
DEFINE_PROP_MIG_CAP("x-auto-converge", MIGRATION_CAPABILITY_AUTO_CONVERGE),
DEFINE_PROP_MIG_CAP("x-zero-blocks", MIGRATION_CAPABILITY_ZERO_BLOCKS),
- DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS),
DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS),
DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM),
DEFINE_PROP_MIG_CAP("x-postcopy-preempt",
MIGRATION_CAPABILITY_POSTCOPY_PREEMPT),
DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO),
DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
- DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK),
DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH),
DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD),
DEFINE_PROP_MIG_CAP("x-background-snapshot",
@@ -225,13 +203,6 @@ bool migrate_background_snapshot(void)
return s->capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT];
}
-bool migrate_block(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->capabilities[MIGRATION_CAPABILITY_BLOCK];
-}
-
bool migrate_colo(void)
{
MigrationState *s = migrate_get_current();
@@ -239,13 +210,6 @@ bool migrate_colo(void)
return s->capabilities[MIGRATION_CAPABILITY_X_COLO];
}
-bool migrate_compress(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->capabilities[MIGRATION_CAPABILITY_COMPRESS];
-}
-
bool migrate_dirty_bitmaps(void)
{
MigrationState *s = migrate_get_current();
@@ -459,7 +423,6 @@ INITIALIZE_MIGRATE_CAPS_SET(check_caps_background_snapshot,
MIGRATION_CAPABILITY_AUTO_CONVERGE,
MIGRATION_CAPABILITY_RELEASE_RAM,
MIGRATION_CAPABILITY_RDMA_PIN_ALL,
- MIGRATION_CAPABILITY_COMPRESS,
MIGRATION_CAPABILITY_XBZRLE,
MIGRATION_CAPABILITY_X_COLO,
MIGRATION_CAPABILITY_VALIDATE_UUID,
@@ -484,24 +447,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
ERRP_GUARD();
MigrationIncomingState *mis = migration_incoming_get_current();
-#ifndef CONFIG_LIVE_BLOCK_MIGRATION
- if (new_caps[MIGRATION_CAPABILITY_BLOCK]) {
- error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
- "block migration");
- error_append_hint(errp, "Use blockdev-mirror with NBD instead.\n");
- return false;
- }
-#endif
- if (new_caps[MIGRATION_CAPABILITY_BLOCK]) {
- warn_report("block migration is deprecated;"
- " use blockdev-mirror with NBD instead");
- }
-
- if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
- warn_report("old compression method is deprecated;"
- " use multifd compression methods instead");
- }
-
#ifndef CONFIG_REPLICATION
if (new_caps[MIGRATION_CAPABILITY_X_COLO]) {
error_setg(errp, "QEMU compiled without replication module"
@@ -570,7 +515,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
#ifdef CONFIG_LINUX
if (new_caps[MIGRATION_CAPABILITY_ZERO_COPY_SEND] &&
(!new_caps[MIGRATION_CAPABILITY_MULTIFD] ||
- new_caps[MIGRATION_CAPABILITY_COMPRESS] ||
new_caps[MIGRATION_CAPABILITY_XBZRLE] ||
migrate_multifd_compression() ||
migrate_tls())) {
@@ -592,17 +536,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
return false;
}
- /*
- * Preempt mode requires urgent pages to be sent in separate
- * channel, OTOH compression logic will disorder all pages into
- * different compression channels, which is not compatible with the
- * preempt assumptions on channel assignments.
- */
- if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
- error_setg(errp, "Postcopy preempt not compatible with compress");
- return false;
- }
-
if (migrate_incoming_started()) {
error_setg(errp,
"Postcopy preempt must be set before incoming starts");
@@ -611,10 +544,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
}
if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
- if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
- error_setg(errp, "Multifd is not compatible with compress");
- return false;
- }
if (migrate_incoming_started()) {
error_setg(errp, "Multifd must be set before incoming starts");
return false;
@@ -649,13 +578,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
}
}
- if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
- if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) {
- error_setg(errp, "Compression is not compatible with xbzrle");
- return false;
- }
- }
-
if (new_caps[MIGRATION_CAPABILITY_MAPPED_RAM]) {
if (new_caps[MIGRATION_CAPABILITY_XBZRLE]) {
error_setg(errp,
@@ -663,12 +585,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
return false;
}
- if (new_caps[MIGRATION_CAPABILITY_COMPRESS]) {
- error_setg(errp,
- "Mapped-ram migration is incompatible with compression");
- return false;
- }
-
if (new_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
error_setg(errp,
"Mapped-ram migration is incompatible with postcopy");
@@ -707,11 +623,6 @@ MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp)
int i;
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
-#ifndef CONFIG_LIVE_BLOCK_MIGRATION
- if (i == MIGRATION_CAPABILITY_BLOCK) {
- continue;
- }
-#endif
caps = g_malloc0(sizeof(*caps));
caps->capability = i;
caps->state = s->capabilities[i];
@@ -763,13 +674,6 @@ bool migrate_has_block_bitmap_mapping(void)
return s->parameters.has_block_bitmap_mapping;
}
-bool migrate_block_incremental(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->parameters.block_incremental;
-}
-
uint32_t migrate_checkpoint_delay(void)
{
MigrationState *s = migrate_get_current();
@@ -777,27 +681,6 @@ uint32_t migrate_checkpoint_delay(void)
return s->parameters.x_checkpoint_delay;
}
-int migrate_compress_level(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->parameters.compress_level;
-}
-
-int migrate_compress_threads(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->parameters.compress_threads;
-}
-
-int migrate_compress_wait_thread(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->parameters.compress_wait_thread;
-}
-
uint8_t migrate_cpu_throttle_increment(void)
{
MigrationState *s = migrate_get_current();
@@ -819,13 +702,6 @@ bool migrate_cpu_throttle_tailslow(void)
return s->parameters.cpu_throttle_tailslow;
}
-int migrate_decompress_threads(void)
-{
- MigrationState *s = migrate_get_current();
-
- return s->parameters.decompress_threads;
-}
-
uint64_t migrate_downtime_limit(void)
{
MigrationState *s = migrate_get_current();
@@ -948,29 +824,8 @@ ZeroPageDetection migrate_zero_page_detection(void)
return s->parameters.zero_page_detection;
}
-/* parameter setters */
-
-void migrate_set_block_incremental(bool value)
-{
- MigrationState *s = migrate_get_current();
-
- s->parameters.block_incremental = value;
-}
-
/* parameters helpers */
-void block_cleanup_parameters(void)
-{
- MigrationState *s = migrate_get_current();
-
- if (s->must_remove_block_options) {
- /* setting to false can never fail */
- migrate_cap_set(MIGRATION_CAPABILITY_BLOCK, false, &error_abort);
- migrate_set_block_incremental(false);
- s->must_remove_block_options = false;
- }
-}
-
AnnounceParameters *migrate_announce_params(void)
{
static AnnounceParameters ap;
@@ -992,14 +847,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
/* TODO use QAPI_CLONE() instead of duplicating it inline */
params = g_malloc0(sizeof(*params));
- params->has_compress_level = true;
- params->compress_level = s->parameters.compress_level;
- params->has_compress_threads = true;
- params->compress_threads = s->parameters.compress_threads;
- params->has_compress_wait_thread = true;
- params->compress_wait_thread = s->parameters.compress_wait_thread;
- params->has_decompress_threads = true;
- params->decompress_threads = s->parameters.decompress_threads;
params->has_throttle_trigger_threshold = true;
params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold;
params->has_cpu_throttle_initial = true;
@@ -1020,8 +867,6 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
params->downtime_limit = s->parameters.downtime_limit;
params->has_x_checkpoint_delay = true;
params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
- params->has_block_incremental = true;
- params->block_incremental = s->parameters.block_incremental;
params->has_multifd_channels = true;
params->multifd_channels = s->parameters.multifd_channels;
params->has_multifd_compression = true;
@@ -1070,10 +915,6 @@ void migrate_params_init(MigrationParameters *params)
params->tls_creds = g_strdup("");
/* Set has_* up only for parameter checks */
- params->has_compress_level = true;
- params->has_compress_threads = true;
- params->has_compress_wait_thread = true;
- params->has_decompress_threads = true;
params->has_throttle_trigger_threshold = true;
params->has_cpu_throttle_initial = true;
params->has_cpu_throttle_increment = true;
@@ -1081,7 +922,6 @@ void migrate_params_init(MigrationParameters *params)
params->has_max_bandwidth = true;
params->has_downtime_limit = true;
params->has_x_checkpoint_delay = true;
- params->has_block_incremental = true;
params->has_multifd_channels = true;
params->has_multifd_compression = true;
params->has_multifd_zlib_level = true;
@@ -1107,27 +947,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
{
ERRP_GUARD();
- if (params->has_compress_level &&
- (params->compress_level > 9)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "compress_level",
- "a value between 0 and 9");
- return false;
- }
-
- if (params->has_compress_threads && (params->compress_threads < 1)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
- "compress_threads",
- "a value between 1 and 255");
- return false;
- }
-
- if (params->has_decompress_threads && (params->decompress_threads < 1)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
- "decompress_threads",
- "a value between 1 and 255");
- return false;
- }
-
if (params->has_throttle_trigger_threshold &&
(params->throttle_trigger_threshold < 1 ||
params->throttle_trigger_threshold > 100)) {
@@ -1301,22 +1120,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
/* TODO use QAPI_CLONE() instead of duplicating it inline */
- if (params->has_compress_level) {
- dest->compress_level = params->compress_level;
- }
-
- if (params->has_compress_threads) {
- dest->compress_threads = params->compress_threads;
- }
-
- if (params->has_compress_wait_thread) {
- dest->compress_wait_thread = params->compress_wait_thread;
- }
-
- if (params->has_decompress_threads) {
- dest->decompress_threads = params->decompress_threads;
- }
-
if (params->has_throttle_trigger_threshold) {
dest->throttle_trigger_threshold = params->throttle_trigger_threshold;
}
@@ -1359,9 +1162,6 @@ static void migrate_params_test_apply(MigrateSetParameters *params,
dest->x_checkpoint_delay = params->x_checkpoint_delay;
}
- if (params->has_block_incremental) {
- dest->block_incremental = params->block_incremental;
- }
if (params->has_multifd_channels) {
dest->multifd_channels = params->multifd_channels;
}
@@ -1424,30 +1224,6 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
/* TODO use QAPI_CLONE() instead of duplicating it inline */
- if (params->has_compress_level) {
- warn_report("old compression is deprecated;"
- " use multifd compression methods instead");
- s->parameters.compress_level = params->compress_level;
- }
-
- if (params->has_compress_threads) {
- warn_report("old compression is deprecated;"
- " use multifd compression methods instead");
- s->parameters.compress_threads = params->compress_threads;
- }
-
- if (params->has_compress_wait_thread) {
- warn_report("old compression is deprecated;"
- " use multifd compression methods instead");
- s->parameters.compress_wait_thread = params->compress_wait_thread;
- }
-
- if (params->has_decompress_threads) {
- warn_report("old compression is deprecated;"
- " use multifd compression methods instead");
- s->parameters.decompress_threads = params->decompress_threads;
- }
-
if (params->has_throttle_trigger_threshold) {
s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold;
}
@@ -1502,11 +1278,6 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
colo_checkpoint_delay_set();
}
- if (params->has_block_incremental) {
- warn_report("block migration is deprecated;"
- " use blockdev-mirror with NBD instead");
- s->parameters.block_incremental = params->block_incremental;
- }
if (params->has_multifd_channels) {
s->parameters.multifd_channels = params->multifd_channels;
}
diff --git a/migration/options.h b/migration/options.h
index ab8199e207..4b21cc2669 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -25,9 +25,7 @@ extern Property migration_properties[];
/* capabilities */
bool migrate_auto_converge(void);
-bool migrate_block(void);
bool migrate_colo(void);
-bool migrate_compress(void);
bool migrate_dirty_bitmaps(void);
bool migrate_events(void);
bool migrate_mapped_ram(void);
@@ -67,15 +65,10 @@ bool migrate_cap_set(int cap, bool value, Error **errp);
const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void);
bool migrate_has_block_bitmap_mapping(void);
-bool migrate_block_incremental(void);
uint32_t migrate_checkpoint_delay(void);
-int migrate_compress_level(void);
-int migrate_compress_threads(void);
-int migrate_compress_wait_thread(void);
uint8_t migrate_cpu_throttle_increment(void);
uint8_t migrate_cpu_throttle_initial(void);
bool migrate_cpu_throttle_tailslow(void);
-int migrate_decompress_threads(void);
uint64_t migrate_downtime_limit(void);
uint8_t migrate_max_cpu_throttle(void);
uint64_t migrate_max_bandwidth(void);
@@ -92,14 +85,8 @@ const char *migrate_tls_hostname(void);
uint64_t migrate_xbzrle_cache_size(void);
ZeroPageDetection migrate_zero_page_detection(void);
-/* parameters setters */
-
-void migrate_set_block_incremental(bool value);
-
/* parameters helpers */
bool migrate_params_check(MigrationParameters *params, Error **errp);
void migrate_params_init(MigrationParameters *params);
-void block_cleanup_parameters(void);
-
#endif
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index eccff499cb..3419779548 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -44,7 +44,7 @@
*/
#define MAX_DISCARDS_PER_COMMAND 12
-struct PostcopyDiscardState {
+typedef struct PostcopyDiscardState {
const char *ramblock_name;
uint16_t cur_entry;
/*
@@ -54,7 +54,7 @@ struct PostcopyDiscardState {
uint64_t length_list[MAX_DISCARDS_PER_COMMAND];
unsigned int nsentwords;
unsigned int nsentcmds;
-};
+} PostcopyDiscardState;
static NotifierWithReturnList postcopy_notifier_list;
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index a10882d47f..9ccbbb0099 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -778,84 +778,6 @@ uint64_t qemu_get_be64(QEMUFile *f)
return v;
}
-/* return the size after compression, or negative value on error */
-static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len,
- const uint8_t *source, size_t source_len)
-{
- int err;
-
- err = deflateReset(stream);
- if (err != Z_OK) {
- return -1;
- }
-
- stream->avail_in = source_len;
- stream->next_in = (uint8_t *)source;
- stream->avail_out = dest_len;
- stream->next_out = dest;
-
- err = deflate(stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- return -1;
- }
-
- return stream->next_out - dest;
-}
-
-/* Compress size bytes of data start at p and store the compressed
- * data to the buffer of f.
- *
- * Since the file is dummy file with empty_ops, return -1 if f has no space to
- * save the compressed data.
- */
-ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
- const uint8_t *p, size_t size)
-{
- ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t);
-
- if (blen < compressBound(size)) {
- return -1;
- }
-
- blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t),
- blen, p, size);
- if (blen < 0) {
- return -1;
- }
-
- qemu_put_be32(f, blen);
- add_buf_to_iovec(f, blen);
- return blen + sizeof(int32_t);
-}
-
-/* Put the data in the buffer of f_src to the buffer of f_des, and
- * then reset the buf_index of f_src to 0.
- */
-
-int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src)
-{
- int len = 0;
-
- if (f_src->buf_index > 0) {
- len = f_src->buf_index;
- qemu_put_buffer(f_des, f_src->buf, f_src->buf_index);
- f_src->buf_index = 0;
- f_src->iovcnt = 0;
- }
- return len;
-}
-
-/*
- * Check if the writable buffer is empty
- */
-
-bool qemu_file_buffer_empty(QEMUFile *file)
-{
- assert(qemu_file_is_writable(file));
-
- return !file->iovcnt;
-}
-
/*
* Get a string whose length is determined by a single preceding byte
* A preallocated 256 byte buffer must be passed in.
diff --git a/migration/qemu-file.h b/migration/qemu-file.h
index 32fd4a34fd..11c2120edd 100644
--- a/migration/qemu-file.h
+++ b/migration/qemu-file.h
@@ -54,10 +54,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size,
size_t coroutine_mixed_fn qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset);
size_t coroutine_mixed_fn qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size);
-ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream,
- const uint8_t *p, size_t size);
-int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src);
-bool qemu_file_buffer_empty(QEMUFile *file);
/*
* Note that you can only peek continuous bytes from where the current pointer
diff --git a/migration/ram-compress.c b/migration/ram-compress.c
deleted file mode 100644
index fa4388f6a6..0000000000
--- a/migration/ram-compress.c
+++ /dev/null
@@ -1,564 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- * Copyright (c) 2011-2015 Red Hat Inc
- *
- * Authors:
- * Juan Quintela <quintela@redhat.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/cutils.h"
-
-#include "ram-compress.h"
-
-#include "qemu/error-report.h"
-#include "qemu/stats64.h"
-#include "migration.h"
-#include "options.h"
-#include "io/channel-null.h"
-#include "exec/target_page.h"
-#include "exec/ramblock.h"
-#include "ram.h"
-#include "migration-stats.h"
-
-static struct {
- int64_t pages;
- int64_t busy;
- double busy_rate;
- int64_t compressed_size;
- double compression_rate;
- /* compression statistics since the beginning of the period */
- /* amount of count that no free thread to compress data */
- uint64_t compress_thread_busy_prev;
- /* amount bytes after compression */
- uint64_t compressed_size_prev;
- /* amount of compressed pages */
- uint64_t compress_pages_prev;
-} compression_counters;
-
-static CompressParam *comp_param;
-static QemuThread *compress_threads;
-/* comp_done_cond is used to wake up the migration thread when
- * one of the compression threads has finished the compression.
- * comp_done_lock is used to co-work with comp_done_cond.
- */
-static QemuMutex comp_done_lock;
-static QemuCond comp_done_cond;
-
-struct DecompressParam {
- bool done;
- bool quit;
- QemuMutex mutex;
- QemuCond cond;
- void *des;
- uint8_t *compbuf;
- int len;
- z_stream stream;
-};
-typedef struct DecompressParam DecompressParam;
-
-static QEMUFile *decomp_file;
-static DecompressParam *decomp_param;
-static QemuThread *decompress_threads;
-static QemuMutex decomp_done_lock;
-static QemuCond decomp_done_cond;
-
-static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream,
- RAMBlock *block, ram_addr_t offset,
- uint8_t *source_buf);
-
-static void *do_data_compress(void *opaque)
-{
- CompressParam *param = opaque;
- RAMBlock *block;
- ram_addr_t offset;
- CompressResult result;
-
- qemu_mutex_lock(&param->mutex);
- while (!param->quit) {
- if (param->trigger) {
- block = param->block;
- offset = param->offset;
- param->trigger = false;
- qemu_mutex_unlock(&param->mutex);
-
- result = do_compress_ram_page(param->file, &param->stream,
- block, offset, param->originbuf);
-
- qemu_mutex_lock(&comp_done_lock);
- param->done = true;
- param->result = result;
- qemu_cond_signal(&comp_done_cond);
- qemu_mutex_unlock(&comp_done_lock);
-
- qemu_mutex_lock(&param->mutex);
- } else {
- qemu_cond_wait(&param->cond, &param->mutex);
- }
- }
- qemu_mutex_unlock(&param->mutex);
-
- return NULL;
-}
-
-void compress_threads_save_cleanup(void)
-{
- int i, thread_count;
-
- if (!migrate_compress() || !comp_param) {
- return;
- }
-
- thread_count = migrate_compress_threads();
- for (i = 0; i < thread_count; i++) {
- /*
- * we use it as a indicator which shows if the thread is
- * properly init'd or not
- */
- if (!comp_param[i].file) {
- break;
- }
-
- qemu_mutex_lock(&comp_param[i].mutex);
- comp_param[i].quit = true;
- qemu_cond_signal(&comp_param[i].cond);
- qemu_mutex_unlock(&comp_param[i].mutex);
-
- qemu_thread_join(compress_threads + i);
- qemu_mutex_destroy(&comp_param[i].mutex);
- qemu_cond_destroy(&comp_param[i].cond);
- deflateEnd(&comp_param[i].stream);
- g_free(comp_param[i].originbuf);
- qemu_fclose(comp_param[i].file);
- comp_param[i].file = NULL;
- }
- qemu_mutex_destroy(&comp_done_lock);
- qemu_cond_destroy(&comp_done_cond);
- g_free(compress_threads);
- g_free(comp_param);
- compress_threads = NULL;
- comp_param = NULL;
-}
-
-int compress_threads_save_setup(void)
-{
- int i, thread_count;
-
- if (!migrate_compress()) {
- return 0;
- }
- thread_count = migrate_compress_threads();
- compress_threads = g_new0(QemuThread, thread_count);
- comp_param = g_new0(CompressParam, thread_count);
- qemu_cond_init(&comp_done_cond);
- qemu_mutex_init(&comp_done_lock);
- for (i = 0; i < thread_count; i++) {
- comp_param[i].originbuf = g_try_malloc(qemu_target_page_size());
- if (!comp_param[i].originbuf) {
- goto exit;
- }
-
- if (deflateInit(&comp_param[i].stream,
- migrate_compress_level()) != Z_OK) {
- g_free(comp_param[i].originbuf);
- goto exit;
- }
-
- /* comp_param[i].file is just used as a dummy buffer to save data,
- * set its ops to empty.
- */
- comp_param[i].file = qemu_file_new_output(
- QIO_CHANNEL(qio_channel_null_new()));
- comp_param[i].done = true;
- comp_param[i].quit = false;
- qemu_mutex_init(&comp_param[i].mutex);
- qemu_cond_init(&comp_param[i].cond);
- qemu_thread_create(compress_threads + i, "compress",
- do_data_compress, comp_param + i,
- QEMU_THREAD_JOINABLE);
- }
- return 0;
-
-exit:
- compress_threads_save_cleanup();
- return -1;
-}
-
-static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream,
- RAMBlock *block, ram_addr_t offset,
- uint8_t *source_buf)
-{
- uint8_t *p = block->host + offset;
- size_t page_size = qemu_target_page_size();
- int ret;
-
- assert(qemu_file_buffer_empty(f));
-
- if (buffer_is_zero(p, page_size)) {
- return RES_ZEROPAGE;
- }
-
- /*
- * copy it to a internal buffer to avoid it being modified by VM
- * so that we can catch up the error during compression and
- * decompression
- */
- memcpy(source_buf, p, page_size);
- ret = qemu_put_compression_data(f, stream, source_buf, page_size);
- if (ret < 0) {
- qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
- error_report("compressed data failed!");
- qemu_fflush(f);
- return RES_NONE;
- }
- return RES_COMPRESS;
-}
-
-static inline void compress_reset_result(CompressParam *param)
-{
- param->result = RES_NONE;
- param->block = NULL;
- param->offset = 0;
-}
-
-void compress_flush_data(void)
-{
- int thread_count = migrate_compress_threads();
-
- if (!migrate_compress()) {
- return;
- }
-
- qemu_mutex_lock(&comp_done_lock);
- for (int i = 0; i < thread_count; i++) {
- while (!comp_param[i].done) {
- qemu_cond_wait(&comp_done_cond, &comp_done_lock);
- }
- }
- qemu_mutex_unlock(&comp_done_lock);
-
- for (int i = 0; i < thread_count; i++) {
- qemu_mutex_lock(&comp_param[i].mutex);
- if (!comp_param[i].quit) {
- CompressParam *param = &comp_param[i];
- compress_send_queued_data(param);
- assert(qemu_file_buffer_empty(param->file));
- compress_reset_result(param);
- }
- qemu_mutex_unlock(&comp_param[i].mutex);
- }
-}
-
-static inline void set_compress_params(CompressParam *param, RAMBlock *block,
- ram_addr_t offset)
-{
- param->block = block;
- param->offset = offset;
- param->trigger = true;
-}
-
-/*
- * Return true when it compress a page
- */
-bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset,
- int (send_queued_data(CompressParam *)))
-{
- int thread_count;
- bool wait = migrate_compress_wait_thread();
-
- thread_count = migrate_compress_threads();
- qemu_mutex_lock(&comp_done_lock);
-
- while (true) {
- for (int i = 0; i < thread_count; i++) {
- if (comp_param[i].done) {
- CompressParam *param = &comp_param[i];
- qemu_mutex_lock(&param->mutex);
- param->done = false;
- send_queued_data(param);
- assert(qemu_file_buffer_empty(param->file));
- compress_reset_result(param);
- set_compress_params(param, block, offset);
-
- qemu_cond_signal(&param->cond);
- qemu_mutex_unlock(&param->mutex);
- qemu_mutex_unlock(&comp_done_lock);
- return true;
- }
- }
- if (!wait) {
- qemu_mutex_unlock(&comp_done_lock);
- compression_counters.busy++;
- return false;
- }
- /*
- * wait for a free thread if the user specifies
- * 'compress-wait-thread', otherwise we will post the page out
- * in the main thread as normal page.
- */
- qemu_cond_wait(&comp_done_cond, &comp_done_lock);
- }
-}
-
-/* return the size after decompression, or negative value on error */
-static int
-qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len,
- const uint8_t *source, size_t source_len)
-{
- int err;
-
- err = inflateReset(stream);
- if (err != Z_OK) {
- return -1;
- }
-
- stream->avail_in = source_len;
- stream->next_in = (uint8_t *)source;
- stream->avail_out = dest_len;
- stream->next_out = dest;
-
- err = inflate(stream, Z_NO_FLUSH);
- if (err != Z_STREAM_END) {
- return -1;
- }
-
- return stream->total_out;
-}
-
-static void *do_data_decompress(void *opaque)
-{
- DecompressParam *param = opaque;
- unsigned long pagesize;
- uint8_t *des;
- int len, ret;
-
- qemu_mutex_lock(&param->mutex);
- while (!param->quit) {
- if (param->des) {
- des = param->des;
- len = param->len;
- param->des = 0;
- qemu_mutex_unlock(&param->mutex);
-
- pagesize = qemu_target_page_size();
-
- ret = qemu_uncompress_data(&param->stream, des, pagesize,
- param->compbuf, len);
- if (ret < 0 && migrate_get_current()->decompress_error_check) {
- error_report("decompress data failed");
- qemu_file_set_error(decomp_file, ret);
- }
-
- qemu_mutex_lock(&decomp_done_lock);
- param->done = true;
- qemu_cond_signal(&decomp_done_cond);
- qemu_mutex_unlock(&decomp_done_lock);
-
- qemu_mutex_lock(&param->mutex);
- } else {
- qemu_cond_wait(&param->cond, &param->mutex);
- }
- }
- qemu_mutex_unlock(&param->mutex);
-
- return NULL;
-}
-
-int wait_for_decompress_done(void)
-{
- if (!migrate_compress()) {
- return 0;
- }
-
- int thread_count = migrate_decompress_threads();
- qemu_mutex_lock(&decomp_done_lock);
- for (int i = 0; i < thread_count; i++) {
- while (!decomp_param[i].done) {
- qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
- }
- }
- qemu_mutex_unlock(&decomp_done_lock);
- return qemu_file_get_error(decomp_file);
-}
-
-void compress_threads_load_cleanup(void)
-{
- int i, thread_count;
-
- if (!migrate_compress()) {
- return;
- }
- thread_count = migrate_decompress_threads();
- for (i = 0; i < thread_count; i++) {
- /*
- * we use it as a indicator which shows if the thread is
- * properly init'd or not
- */
- if (!decomp_param[i].compbuf) {
- break;
- }
-
- qemu_mutex_lock(&decomp_param[i].mutex);
- decomp_param[i].quit = true;
- qemu_cond_signal(&decomp_param[i].cond);
- qemu_mutex_unlock(&decomp_param[i].mutex);
- }
- for (i = 0; i < thread_count; i++) {
- if (!decomp_param[i].compbuf) {
- break;
- }
-
- qemu_thread_join(decompress_threads + i);
- qemu_mutex_destroy(&decomp_param[i].mutex);
- qemu_cond_destroy(&decomp_param[i].cond);
- inflateEnd(&decomp_param[i].stream);
- g_free(decomp_param[i].compbuf);
- decomp_param[i].compbuf = NULL;
- }
- g_free(decompress_threads);
- g_free(decomp_param);
- decompress_threads = NULL;
- decomp_param = NULL;
- decomp_file = NULL;
-}
-
-int compress_threads_load_setup(QEMUFile *f)
-{
- int i, thread_count;
-
- if (!migrate_compress()) {
- return 0;
- }
-
- /*
- * set compression_counters memory to zero for a new migration
- */
- memset(&compression_counters, 0, sizeof(compression_counters));
-
- thread_count = migrate_decompress_threads();
- decompress_threads = g_new0(QemuThread, thread_count);
- decomp_param = g_new0(DecompressParam, thread_count);
- qemu_mutex_init(&decomp_done_lock);
- qemu_cond_init(&decomp_done_cond);
- decomp_file = f;
- for (i = 0; i < thread_count; i++) {
- if (inflateInit(&decomp_param[i].stream) != Z_OK) {
- goto exit;
- }
-
- size_t compbuf_size = compressBound(qemu_target_page_size());
- decomp_param[i].compbuf = g_malloc0(compbuf_size);
- qemu_mutex_init(&decomp_param[i].mutex);
- qemu_cond_init(&decomp_param[i].cond);
- decomp_param[i].done = true;
- decomp_param[i].quit = false;
- qemu_thread_create(decompress_threads + i, "decompress",
- do_data_decompress, decomp_param + i,
- QEMU_THREAD_JOINABLE);
- }
- return 0;
-exit:
- compress_threads_load_cleanup();
- return -1;
-}
-
-void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len)
-{
- int thread_count = migrate_decompress_threads();
- QEMU_LOCK_GUARD(&decomp_done_lock);
- while (true) {
- for (int i = 0; i < thread_count; i++) {
- if (decomp_param[i].done) {
- decomp_param[i].done = false;
- qemu_mutex_lock(&decomp_param[i].mutex);
- qemu_get_buffer(f, decomp_param[i].compbuf, len);
- decomp_param[i].des = host;
- decomp_param[i].len = len;
- qemu_cond_signal(&decomp_param[i].cond);
- qemu_mutex_unlock(&decomp_param[i].mutex);
- return;
- }
- }
- qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
- }
-}
-
-void populate_compress(MigrationInfo *info)
-{
- if (!migrate_compress()) {
- return;
- }
- info->compression = g_malloc0(sizeof(*info->compression));
- info->compression->pages = compression_counters.pages;
- info->compression->busy = compression_counters.busy;
- info->compression->busy_rate = compression_counters.busy_rate;
- info->compression->compressed_size = compression_counters.compressed_size;
- info->compression->compression_rate = compression_counters.compression_rate;
-}
-
-uint64_t compress_ram_pages(void)
-{
- return compression_counters.pages;
-}
-
-void update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
-{
- ram_transferred_add(bytes_xmit);
-
- if (param->result == RES_ZEROPAGE) {
- stat64_add(&mig_stats.zero_pages, 1);
- return;
- }
-
- /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */
- compression_counters.compressed_size += bytes_xmit - 8;
- compression_counters.pages++;
-}
-
-void compress_update_rates(uint64_t page_count)
-{
- if (!migrate_compress()) {
- return;
- }
- compression_counters.busy_rate = (double)(compression_counters.busy -
- compression_counters.compress_thread_busy_prev) / page_count;
- compression_counters.compress_thread_busy_prev =
- compression_counters.busy;
-
- double compressed_size = compression_counters.compressed_size -
- compression_counters.compressed_size_prev;
- if (compressed_size) {
- double uncompressed_size = (compression_counters.pages -
- compression_counters.compress_pages_prev) *
- qemu_target_page_size();
-
- /* Compression-Ratio = Uncompressed-size / Compressed-size */
- compression_counters.compression_rate =
- uncompressed_size / compressed_size;
-
- compression_counters.compress_pages_prev =
- compression_counters.pages;
- compression_counters.compressed_size_prev =
- compression_counters.compressed_size;
- }
-}
diff --git a/migration/ram-compress.h b/migration/ram-compress.h
deleted file mode 100644
index 0d89a2f55e..0000000000
--- a/migration/ram-compress.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- * Copyright (c) 2011-2015 Red Hat Inc
- *
- * Authors:
- * Juan Quintela <quintela@redhat.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#ifndef QEMU_MIGRATION_COMPRESS_H
-#define QEMU_MIGRATION_COMPRESS_H
-
-#include "qemu-file.h"
-#include "qapi/qapi-types-migration.h"
-
-enum CompressResult {
- RES_NONE = 0,
- RES_ZEROPAGE = 1,
- RES_COMPRESS = 2
-};
-typedef enum CompressResult CompressResult;
-
-struct CompressParam {
- bool done;
- bool quit;
- bool trigger;
- CompressResult result;
- QEMUFile *file;
- QemuMutex mutex;
- QemuCond cond;
- RAMBlock *block;
- ram_addr_t offset;
-
- /* internally used fields */
- z_stream stream;
- uint8_t *originbuf;
-};
-typedef struct CompressParam CompressParam;
-
-void compress_threads_save_cleanup(void);
-int compress_threads_save_setup(void);
-
-bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset,
- int (send_queued_data(CompressParam *)));
-
-int wait_for_decompress_done(void);
-void compress_threads_load_cleanup(void);
-int compress_threads_load_setup(QEMUFile *f);
-void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len);
-
-void populate_compress(MigrationInfo *info);
-uint64_t compress_ram_pages(void);
-void update_compress_thread_counts(const CompressParam *param, int bytes_xmit);
-void compress_update_rates(uint64_t page_count);
-int compress_send_queued_data(CompressParam *param);
-void compress_flush_data(void);
-
-#endif
diff --git a/migration/ram.c b/migration/ram.c
index a975c5af16..ceea586b06 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -33,7 +33,6 @@
#include "qemu/madvise.h"
#include "qemu/main-loop.h"
#include "xbzrle.h"
-#include "ram-compress.h"
#include "ram.h"
#include "migration.h"
#include "migration-stats.h"
@@ -53,7 +52,6 @@
#include "exec/target_page.h"
#include "qemu/rcu_queue.h"
#include "migration/colo.h"
-#include "block.h"
#include "sysemu/cpu-throttle.h"
#include "savevm.h"
#include "qemu/iov.h"
@@ -78,9 +76,10 @@
* worked for pages that were filled with the same char. We switched
* it to only search for the zero value. And to avoid confusion with
* RAM_SAVE_FLAG_COMPRESS_PAGE just rename it.
- */
-/*
- * RAM_SAVE_FLAG_FULL was obsoleted in 2009, it can be reused now
+ *
+ * RAM_SAVE_FLAG_FULL was obsoleted in 2009.
+ *
+ * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1.
*/
#define RAM_SAVE_FLAG_FULL 0x01
#define RAM_SAVE_FLAG_ZERO 0x02
@@ -90,7 +89,6 @@
#define RAM_SAVE_FLAG_CONTINUE 0x20
#define RAM_SAVE_FLAG_XBZRLE 0x40
/* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */
-#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
#define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200
/* We can't use any flag that is bigger than 0x200 */
@@ -691,8 +689,7 @@ static int save_xbzrle_page(RAMState *rs, PageSearchStatus *pss,
qemu_put_buffer(file, XBZRLE.encoded_buf, encoded_len);
bytes_xbzrle += encoded_len + 1 + 2;
/*
- * Like compressed_size (please see update_compress_thread_counts),
- * the xbzrle encoded bytes don't count the 8 byte header with
+ * The xbzrle encoded bytes don't count the 8 byte header with
* RAM_SAVE_FLAG_CONTINUE.
*/
xbzrle_counters.bytes += bytes_xbzrle - 8;
@@ -950,7 +947,7 @@ uint64_t ram_get_total_transferred_pages(void)
{
return stat64_get(&mig_stats.normal_pages) +
stat64_get(&mig_stats.zero_pages) +
- compress_ram_pages() + xbzrle_counters.pages;
+ xbzrle_counters.pages;
}
static void migration_update_rates(RAMState *rs, int64_t end_time)
@@ -983,7 +980,6 @@ static void migration_update_rates(RAMState *rs, int64_t end_time)
rs->xbzrle_pages_prev = xbzrle_counters.pages;
rs->xbzrle_bytes_prev = xbzrle_counters.bytes;
}
- compress_update_rates(page_count);
}
/*
@@ -1025,13 +1021,6 @@ static void migration_trigger_throttle(RAMState *rs)
uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE;
uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100;
- /* During block migration the auto-converge logic incorrectly detects
- * that ram migration makes no progress. Avoid this by disabling the
- * throttling logic during the bulk phase of block migration. */
- if (blk_mig_bulk_active()) {
- return;
- }
-
/*
* The following detection logic can be refined later. For now:
* Check to see if the ratio between dirtied bytes and the approx.
@@ -1066,14 +1055,14 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage)
trace_migration_bitmap_sync_start();
memory_global_dirty_log_sync(last_stage);
- qemu_mutex_lock(&rs->bitmap_mutex);
- WITH_RCU_READ_LOCK_GUARD() {
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- ramblock_sync_dirty_bitmap(rs, block);
+ WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) {
+ WITH_RCU_READ_LOCK_GUARD() {
+ RAMBLOCK_FOREACH_NOT_IGNORED(block) {
+ ramblock_sync_dirty_bitmap(rs, block);
+ }
+ stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining());
}
- stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining());
}
- qemu_mutex_unlock(&rs->bitmap_mutex);
memory_global_after_dirty_log_sync();
trace_migration_bitmap_sync_end(rs->num_dirty_pages_period);
@@ -1296,41 +1285,6 @@ static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset)
return 1;
}
-int compress_send_queued_data(CompressParam *param)
-{
- PageSearchStatus *pss = &ram_state->pss[RAM_CHANNEL_PRECOPY];
- MigrationState *ms = migrate_get_current();
- QEMUFile *file = ms->to_dst_file;
- int len = 0;
-
- RAMBlock *block = param->block;
- ram_addr_t offset = param->offset;
-
- if (param->result == RES_NONE) {
- return 0;
- }
-
- assert(block == pss->last_sent_block);
-
- if (param->result == RES_ZEROPAGE) {
- assert(qemu_file_buffer_empty(param->file));
- len += save_page_header(pss, file, block, offset | RAM_SAVE_FLAG_ZERO);
- qemu_put_byte(file, 0);
- len += 1;
- ram_release_page(block->idstr, offset);
- } else if (param->result == RES_COMPRESS) {
- assert(!qemu_file_buffer_empty(param->file));
- len += save_page_header(pss, file, block,
- offset | RAM_SAVE_FLAG_COMPRESS_PAGE);
- len += qemu_put_qemu_file(file, param->file);
- } else {
- abort();
- }
-
- update_compress_thread_counts(param, len);
-
- return len;
-}
#define PAGE_ALL_CLEAN 0
#define PAGE_TRY_AGAIN 1
@@ -1382,16 +1336,6 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss)
qemu_fflush(f);
}
}
- /*
- * If memory migration starts over, we will meet a dirtied page
- * which may still exists in compression threads's ring, so we
- * should flush the compressed data to make sure the new page
- * is not overwritten by the old one in the destination.
- *
- * Also If xbzrle is on, stop using the data compression at this
- * point. In theory, xbzrle can do better than compression.
- */
- compress_flush_data();
/* Hit the end of the list */
pss->block = QLIST_FIRST_RCU(&ram_list.blocks);
@@ -2042,37 +1986,6 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len,
return 0;
}
-/*
- * try to compress the page before posting it out, return true if the page
- * has been properly handled by compression, otherwise needs other
- * paths to handle it
- */
-static bool save_compress_page(RAMState *rs, PageSearchStatus *pss,
- ram_addr_t offset)
-{
- if (!migrate_compress()) {
- return false;
- }
-
- /*
- * When starting the process of a new block, the first page of
- * the block should be sent out before other pages in the same
- * block, and all the pages in last block should have been sent
- * out, keeping this order is important, because the 'cont' flag
- * is used to avoid resending the block name.
- *
- * We post the fist page as normal page as compression will take
- * much CPU resource.
- */
- if (pss->block != pss->last_sent_block) {
- compress_flush_data();
- return false;
- }
-
- return compress_page_with_multi_thread(pss->block, offset,
- compress_send_queued_data);
-}
-
/**
* ram_save_target_page_legacy: save one target page
*
@@ -2090,10 +2003,6 @@ static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss)
return res;
}
- if (save_compress_page(rs, pss, offset)) {
- return 1;
- }
-
if (save_zero_page(rs, pss, offset)) {
return 1;
}
@@ -2478,7 +2387,6 @@ static void ram_save_cleanup(void *opaque)
ram_bitmaps_destroy();
xbzrle_cleanup();
- compress_threads_save_cleanup();
ram_state_cleanup(rsp);
g_free(migration_ops);
migration_ops = NULL;
@@ -3097,15 +3005,9 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp)
RAMBlock *block;
int ret, max_hg_page_size;
- if (compress_threads_save_setup()) {
- error_setg(errp, "%s: failed to start compress threads", __func__);
- return -1;
- }
-
/* migration has already setup the bitmap, reuse it. */
if (!migration_in_colo_state()) {
if (ram_init_all(rsp, errp) != 0) {
- compress_threads_save_cleanup();
return -1;
}
}
@@ -3230,13 +3132,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
int64_t t0;
int done = 0;
- if (blk_mig_bulk_active()) {
- /* Avoid transferring ram during bulk phase of block migration as
- * the bulk phase will usually take a long time and transferring
- * ram updates during that time is pointless. */
- goto out;
- }
-
/*
* We'll take this lock a little bit long, but it's okay for two reasons.
* Firstly, the only possible other thread to take it is who calls
@@ -3284,14 +3179,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
rs->target_page_count += pages;
/*
- * During postcopy, it is necessary to make sure one whole host
- * page is sent in one chunk.
- */
- if (migrate_postcopy_ram()) {
- compress_flush_data();
- }
-
- /*
* we want to check in the 1st loop, just in case it was the 1st
* time and we had to sync the dirty bitmap.
* qemu_clock_get_ns() is a bit expensive, so we only check each
@@ -3389,8 +3276,6 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
}
qemu_mutex_unlock(&rs->bitmap_mutex);
- compress_flush_data();
-
ret = rdma_registration_stop(f, RAM_CONTROL_FINISH);
if (ret < 0) {
qemu_file_set_error(f, ret);
@@ -3804,7 +3689,6 @@ int ram_load_postcopy(QEMUFile *f, int channel)
void *place_source = NULL;
RAMBlock *block = NULL;
uint8_t ch;
- int len;
addr = qemu_get_be64(f);
@@ -3821,8 +3705,7 @@ int ram_load_postcopy(QEMUFile *f, int channel)
addr &= TARGET_PAGE_MASK;
trace_ram_load_postcopy_loop(channel, (uint64_t)addr, flags);
- if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
- RAM_SAVE_FLAG_COMPRESS_PAGE)) {
+ if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE)) {
block = ram_block_from_stream(mis, f, flags, channel);
if (!block) {
ret = -EINVAL;
@@ -3917,16 +3800,6 @@ int ram_load_postcopy(QEMUFile *f, int channel)
TARGET_PAGE_SIZE);
}
break;
- case RAM_SAVE_FLAG_COMPRESS_PAGE:
- tmp_page->all_zero = false;
- len = qemu_get_be32(f);
- if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
- error_report("Invalid compressed data length: %d", len);
- ret = -EINVAL;
- break;
- }
- decompress_data_with_multi_threads(f, page_buffer, len);
- break;
case RAM_SAVE_FLAG_MULTIFD_FLUSH:
multifd_recv_sync_main();
break;
@@ -3944,11 +3817,6 @@ int ram_load_postcopy(QEMUFile *f, int channel)
break;
}
- /* Got the whole host page, wait for decompress before placing. */
- if (place_needed) {
- ret |= wait_for_decompress_done();
- }
-
/* Detect for any possible file errors */
if (!ret && qemu_file_get_error(f)) {
ret = qemu_file_get_error(f);
@@ -4253,11 +4121,7 @@ static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes)
static int ram_load_precopy(QEMUFile *f)
{
MigrationIncomingState *mis = migration_incoming_get_current();
- int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;
-
- if (!migrate_compress()) {
- invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE;
- }
+ int flags = 0, ret = 0, invalid_flags = 0, i = 0;
if (migrate_mapped_ram()) {
invalid_flags |= (RAM_SAVE_FLAG_HOOK | RAM_SAVE_FLAG_MULTIFD_FLUSH |
@@ -4294,16 +4158,12 @@ static int ram_load_precopy(QEMUFile *f)
if (flags & invalid_flags) {
error_report("Unexpected RAM flags: %d", flags & invalid_flags);
- if (flags & invalid_flags & RAM_SAVE_FLAG_COMPRESS_PAGE) {
- error_report("Received an unexpected compressed page");
- }
-
ret = -EINVAL;
break;
}
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
- RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
+ RAM_SAVE_FLAG_XBZRLE)) {
RAMBlock *block = ram_block_from_stream(mis, f, flags,
RAM_CHANNEL_PRECOPY);
@@ -4372,16 +4232,6 @@ static int ram_load_precopy(QEMUFile *f)
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
break;
- case RAM_SAVE_FLAG_COMPRESS_PAGE:
- len = qemu_get_be32(f);
- if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
- error_report("Invalid compressed data length: %d", len);
- ret = -EINVAL;
- break;
- }
- decompress_data_with_multi_threads(f, host, len);
- break;
-
case RAM_SAVE_FLAG_XBZRLE:
if (load_xbzrle(f, addr, host) < 0) {
error_report("Failed to decompress XBZRLE page at "
@@ -4423,7 +4273,6 @@ static int ram_load_precopy(QEMUFile *f)
}
}
- ret |= wait_for_decompress_done();
return ret;
}
diff --git a/migration/savevm.c b/migration/savevm.c
index 4509482ec4..6c789bd54b 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1711,11 +1711,6 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
return -EINVAL;
}
- if (migrate_block()) {
- error_setg(errp, "Block migration and snapshots are incompatible");
- return -EINVAL;
- }
-
ret = migrate_init(ms, errp);
if (ret) {
return ret;
diff --git a/migration/trace-events b/migration/trace-events
index f0e1cb80c7..0b7c3324fb 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -58,7 +58,7 @@ postcopy_page_req_sync(void *host_addr) "sync page req %p"
vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d"
vmstate_load_state(const char *name, int version_id) "%s v%d"
vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d"
-vmstate_load_state_field(const char *name, const char *field) "%s:%s"
+vmstate_load_state_field(const char *name, const char *field, bool exists) "%s:%s exists=%d"
vmstate_n_elems(const char *name, int n_elems) "%s: %d"
vmstate_subsection_load(const char *parent) "%s"
vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s"
@@ -152,7 +152,7 @@ multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostnam
# migration.c
migrate_set_state(const char *new_state) "new state %s"
migrate_fd_cleanup(void) ""
-migrate_fd_error(const char *error_desc) "error=%s"
+migrate_error(const char *error_desc) "error=%s"
migrate_fd_cancel(void) ""
migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx"
migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")"
diff --git a/migration/vmstate.c b/migration/vmstate.c
index ef26f26ccd..ff5d589a6d 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -128,8 +128,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
}
}
while (field->name) {
- trace_vmstate_load_state_field(vmsd->name, field->name);
- if (vmstate_field_exists(vmsd, field, opaque, version_id)) {
+ bool exists = vmstate_field_exists(vmsd, field, opaque, version_id);
+ trace_vmstate_load_state_field(vmsd->name, field->name, exists);
+ if (exists) {
void *first_elem = opaque + field->offset;
int i, n_elems = vmstate_n_elems(opaque, field);
int size = vmstate_size(opaque, field);
@@ -478,7 +479,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
len = qemu_peek_byte(f, 1);
if (len < strlen(vmsd->name) + 1) {
- /* subsection name has be be "section_name/a" */
+ /* subsection name has to be "section_name/a" */
trace_vmstate_subsection_load_bad(vmsd->name, "(short)", "");
return 0;
}
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 871898ac46..ea79148ee8 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -22,6 +22,7 @@
#include "monitor/monitor-internal.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-control.h"
+#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qmp/qdict.h"
#include "qemu/cutils.h"
@@ -443,3 +444,19 @@ void hmp_info_mtree(Monitor *mon, const QDict *qdict)
mtree_info(flatview, dispatch_tree, owner, disabled);
}
+
+#if defined(CONFIG_FDT)
+void hmp_dumpdtb(Monitor *mon, const QDict *qdict)
+{
+ const char *filename = qdict_get_str(qdict, "filename");
+ Error *local_err = NULL;
+
+ qmp_dumpdtb(filename, &local_err);
+
+ if (hmp_handle_error(mon, local_err)) {
+ return;
+ }
+
+ monitor_printf(mon, "dtb dumped to %s", filename);
+}
+#endif
diff --git a/plugins/api.c b/plugins/api.c
index 8fa5a600ac..5a0a7f8c71 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -42,10 +42,11 @@
#include "tcg/tcg.h"
#include "exec/exec-all.h"
#include "exec/gdbstub.h"
-#include "exec/ram_addr.h"
+#include "exec/translator.h"
#include "disas/disas.h"
#include "plugin.h"
#ifndef CONFIG_USER_ONLY
+#include "exec/ram_addr.h"
#include "qemu/plugin-memory.h"
#include "hw/boards.h"
#else
@@ -86,19 +87,38 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb);
}
+static bool tb_is_mem_only(void)
+{
+ return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY;
+}
+
void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags,
void *udata)
{
- if (!tb->mem_only) {
- int index = flags == QEMU_PLUGIN_CB_R_REGS ||
- flags == QEMU_PLUGIN_CB_RW_REGS ?
- PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR;
+ if (!tb_is_mem_only()) {
+ plugin_register_dyn_cb__udata(&tb->cbs, cb, flags, udata);
+ }
+}
- plugin_register_dyn_cb__udata(&tb->cbs[index],
- cb, flags, udata);
+void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *udata)
+{
+ if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) {
+ return;
+ }
+ if (cond == QEMU_PLUGIN_COND_ALWAYS) {
+ qemu_plugin_register_vcpu_tb_exec_cb(tb, cb, flags, udata);
+ return;
}
+ plugin_register_dyn_cond_cb__udata(&tb->cbs, cb, flags,
+ cond, entry, imm, udata);
}
void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
@@ -107,9 +127,8 @@ void qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
qemu_plugin_u64 entry,
uint64_t imm)
{
- if (!tb->mem_only) {
- plugin_register_inline_op_on_entry(
- &tb->cbs[PLUGIN_CB_INLINE], 0, op, entry, imm);
+ if (!tb_is_mem_only()) {
+ plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm);
}
}
@@ -118,14 +137,29 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
enum qemu_plugin_cb_flags flags,
void *udata)
{
- if (!insn->mem_only) {
- int index = flags == QEMU_PLUGIN_CB_R_REGS ||
- flags == QEMU_PLUGIN_CB_RW_REGS ?
- PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR;
+ if (!tb_is_mem_only()) {
+ plugin_register_dyn_cb__udata(&insn->insn_cbs, cb, flags, udata);
+ }
+}
- plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][index],
- cb, flags, udata);
+void qemu_plugin_register_vcpu_insn_exec_cond_cb(
+ struct qemu_plugin_insn *insn,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *udata)
+{
+ if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) {
+ return;
+ }
+ if (cond == QEMU_PLUGIN_COND_ALWAYS) {
+ qemu_plugin_register_vcpu_insn_exec_cb(insn, cb, flags, udata);
+ return;
}
+ plugin_register_dyn_cond_cb__udata(&insn->insn_cbs, cb, flags,
+ cond, entry, imm, udata);
}
void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
@@ -134,9 +168,8 @@ void qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
qemu_plugin_u64 entry,
uint64_t imm)
{
- if (!insn->mem_only) {
- plugin_register_inline_op_on_entry(
- &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], 0, op, entry, imm);
+ if (!tb_is_mem_only()) {
+ plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm);
}
}
@@ -151,8 +184,7 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
enum qemu_plugin_mem_rw rw,
void *udata)
{
- plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR],
- cb, flags, rw, udata);
+ plugin_register_vcpu_mem_cb(&insn->mem_cbs, cb, flags, rw, udata);
}
void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
@@ -162,8 +194,7 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
qemu_plugin_u64 entry,
uint64_t imm)
{
- plugin_register_inline_op_on_entry(
- &insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE], rw, op, entry, imm);
+ plugin_register_inline_op_on_entry(&insn->mem_cbs, rw, op, entry, imm);
}
void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
@@ -208,7 +239,8 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb)
uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb)
{
- return tb->vaddr;
+ const DisasContextBase *db = tcg_ctx->plugin_db;
+ return db->pc_first;
}
struct qemu_plugin_insn *
@@ -219,7 +251,6 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx)
return NULL;
}
insn = g_ptr_array_index(tb->insns, idx);
- insn->mem_only = tb->mem_only;
return insn;
}
@@ -230,14 +261,18 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx)
* instruction being translated.
*/
-const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn)
+size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn,
+ void *dest, size_t len)
{
- return insn->data->data;
+ const DisasContextBase *db = tcg_ctx->plugin_db;
+
+ len = MIN(len, insn->len);
+ return translator_st(db, dest, insn->vaddr, len) ? len : 0;
}
size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn)
{
- return insn->data->len;
+ return insn->len;
}
uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn)
@@ -247,13 +282,36 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn)
void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
{
- return insn->haddr;
+ const DisasContextBase *db = tcg_ctx->plugin_db;
+ vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK;
+
+ if (db->fake_insn) {
+ return NULL;
+ }
+
+ /*
+ * ??? The return value is not intended for use of host memory,
+ * but as a proxy for address space and physical address.
+ * Thus we are only interested in the first byte and do not
+ * care about spanning pages.
+ */
+ if (insn->vaddr <= page0_last) {
+ if (db->host_addr[0] == NULL) {
+ return NULL;
+ }
+ return db->host_addr[0] + insn->vaddr - db->pc_first;
+ } else {
+ if (db->host_addr[1] == NULL) {
+ return NULL;
+ }
+ return db->host_addr[1] + insn->vaddr - (page0_last + 1);
+ }
}
char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn)
{
- CPUState *cpu = current_cpu;
- return plugin_disas(cpu, insn->vaddr, insn->data->len);
+ return plugin_disas(tcg_ctx->cpu, tcg_ctx->plugin_db,
+ insn->vaddr, insn->len);
}
const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn)
diff --git a/plugins/core.c b/plugins/core.c
index 11ca20e626..0726bc7f25 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -307,7 +307,7 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
GArray *cbs = *arr;
if (!cbs) {
- cbs = g_array_sized_new(false, false,
+ cbs = g_array_sized_new(false, true,
sizeof(struct qemu_plugin_dyn_cb), 1);
*arr = cbs;
}
@@ -316,6 +316,18 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
}
+static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
+{
+ switch (op) {
+ case QEMU_PLUGIN_INLINE_ADD_U64:
+ return PLUGIN_CB_INLINE_ADD_U64;
+ case QEMU_PLUGIN_INLINE_STORE_U64:
+ return PLUGIN_CB_INLINE_STORE_U64;
+ default:
+ g_assert_not_reached();
+ }
+}
+
void plugin_register_inline_op_on_entry(GArray **arr,
enum qemu_plugin_mem_rw rw,
enum qemu_plugin_op op,
@@ -324,13 +336,12 @@ void plugin_register_inline_op_on_entry(GArray **arr,
{
struct qemu_plugin_dyn_cb *dyn_cb;
+ struct qemu_plugin_inline_cb inline_cb = { .rw = rw,
+ .entry = entry,
+ .imm = imm };
dyn_cb = plugin_get_dyn_cb(arr);
- dyn_cb->userp = NULL;
- dyn_cb->type = PLUGIN_CB_INLINE;
- dyn_cb->rw = rw;
- dyn_cb->inline_insn.entry = entry;
- dyn_cb->inline_insn.op = op;
- dyn_cb->inline_insn.imm = imm;
+ dyn_cb->type = op_to_cb_type(op);
+ dyn_cb->inline_insn = inline_cb;
}
void plugin_register_dyn_cb__udata(GArray **arr,
@@ -338,12 +349,57 @@ void plugin_register_dyn_cb__udata(GArray **arr,
enum qemu_plugin_cb_flags flags,
void *udata)
{
- struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+ static TCGHelperInfo info[3] = {
+ [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
+ [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ /*
+ * Match qemu_plugin_vcpu_udata_cb_t:
+ * void (*)(uint32_t, void *)
+ */
+ [0 ... 2].typemask = (dh_typemask(void, 0) |
+ dh_typemask(i32, 1) |
+ dh_typemask(ptr, 2))
+ };
+ assert((unsigned)flags < ARRAY_SIZE(info));
- dyn_cb->userp = udata;
- /* Note flags are discarded as unused. */
- dyn_cb->f.vcpu_udata = cb;
+ struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+ struct qemu_plugin_regular_cb regular_cb = { .f.vcpu_udata = cb,
+ .userp = udata,
+ .info = &info[flags] };
dyn_cb->type = PLUGIN_CB_REGULAR;
+ dyn_cb->regular = regular_cb;
+}
+
+void plugin_register_dyn_cond_cb__udata(GArray **arr,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *udata)
+{
+ static TCGHelperInfo info[3] = {
+ [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
+ [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ /*
+ * Match qemu_plugin_vcpu_udata_cb_t:
+ * void (*)(uint32_t, void *)
+ */
+ [0 ... 2].typemask = (dh_typemask(void, 0) |
+ dh_typemask(i32, 1) |
+ dh_typemask(ptr, 2))
+ };
+ assert((unsigned)flags < ARRAY_SIZE(info));
+
+ struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+ struct qemu_plugin_conditional_cb cond_cb = { .userp = udata,
+ .f.vcpu_udata = cb,
+ .cond = cond,
+ .entry = entry,
+ .imm = imm,
+ .info = &info[flags] };
+ dyn_cb->type = PLUGIN_CB_COND;
+ dyn_cb->cond = cond_cb;
}
void plugin_register_vcpu_mem_cb(GArray **arr,
@@ -352,14 +408,38 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
enum qemu_plugin_mem_rw rw,
void *udata)
{
- struct qemu_plugin_dyn_cb *dyn_cb;
+ /*
+ * Expect that the underlying type for enum qemu_plugin_meminfo_t
+ * is either int32_t or uint32_t, aka int or unsigned int.
+ */
+ QEMU_BUILD_BUG_ON(
+ !__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) &&
+ !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t));
+
+ static TCGHelperInfo info[3] = {
+ [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
+ [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
+ /*
+ * Match qemu_plugin_vcpu_mem_cb_t:
+ * void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
+ */
+ [0 ... 2].typemask =
+ (dh_typemask(void, 0) |
+ dh_typemask(i32, 1) |
+ (__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t)
+ ? dh_typemask(i32, 2) : dh_typemask(s32, 2)) |
+ dh_typemask(i64, 3) |
+ dh_typemask(ptr, 4))
+ };
+ assert((unsigned)flags < ARRAY_SIZE(info));
- dyn_cb = plugin_get_dyn_cb(arr);
- dyn_cb->userp = udata;
- /* Note flags are discarded as unused. */
- dyn_cb->type = PLUGIN_CB_REGULAR;
- dyn_cb->rw = rw;
- dyn_cb->f.generic = cb;
+ struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+ struct qemu_plugin_regular_cb regular_cb = { .userp = udata,
+ .rw = rw,
+ .f.vcpu_mem = cb,
+ .info = &info[flags] };
+ dyn_cb->type = PLUGIN_CB_MEM_REGULAR;
+ dyn_cb->regular = regular_cb;
}
/*
@@ -373,7 +453,7 @@ void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb)
struct qemu_plugin_cb *cb, *next;
enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS;
- /* no plugin_mask check here; caller should have checked */
+ /* no plugin_state->event_mask check here; caller should have checked */
QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans;
@@ -476,17 +556,22 @@ void qemu_plugin_flush_cb(void)
plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
}
-void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index)
+void exec_inline_op(enum plugin_dyn_cb_type type,
+ struct qemu_plugin_inline_cb *cb,
+ int cpu_index)
{
- char *ptr = cb->inline_insn.entry.score->data->data;
+ char *ptr = cb->entry.score->data->data;
size_t elem_size = g_array_get_element_size(
- cb->inline_insn.entry.score->data);
- size_t offset = cb->inline_insn.entry.offset;
+ cb->entry.score->data);
+ size_t offset = cb->entry.offset;
uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size);
- switch (cb->inline_insn.op) {
- case QEMU_PLUGIN_INLINE_ADD_U64:
- *val += cb->inline_insn.imm;
+ switch (type) {
+ case PLUGIN_CB_INLINE_ADD_U64:
+ *val += cb->imm;
+ break;
+ case PLUGIN_CB_INLINE_STORE_U64:
+ *val = cb->imm;
break;
default:
g_assert_not_reached();
@@ -496,7 +581,7 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index)
void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
MemOpIdx oi, enum qemu_plugin_mem_rw rw)
{
- GArray *arr = cpu->plugin_mem_cbs;
+ GArray *arr = cpu->neg.plugin_mem_cbs;
size_t i;
if (arr == NULL) {
@@ -506,16 +591,19 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
struct qemu_plugin_dyn_cb *cb =
&g_array_index(arr, struct qemu_plugin_dyn_cb, i);
- if (!(rw & cb->rw)) {
- break;
- }
switch (cb->type) {
- case PLUGIN_CB_REGULAR:
- cb->f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
- vaddr, cb->userp);
+ case PLUGIN_CB_MEM_REGULAR:
+ if (rw && cb->regular.rw) {
+ cb->regular.f.vcpu_mem(cpu->cpu_index,
+ make_plugin_meminfo(oi, rw),
+ vaddr, cb->regular.userp);
+ }
break;
- case PLUGIN_CB_INLINE:
- exec_inline_op(cb, cpu->cpu_index);
+ case PLUGIN_CB_INLINE_ADD_U64:
+ case PLUGIN_CB_INLINE_STORE_U64:
+ if (rw && cb->inline_insn.rw) {
+ exec_inline_op(cb->type, &cb->inline_insn, cpu->cpu_index);
+ }
break;
default:
g_assert_not_reached();
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 7c34f23cfc..30e2299a54 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -93,6 +93,14 @@ plugin_register_dyn_cb__udata(GArray **arr,
qemu_plugin_vcpu_udata_cb_t cb,
enum qemu_plugin_cb_flags flags, void *udata);
+void
+plugin_register_dyn_cond_cb__udata(GArray **arr,
+ qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags,
+ enum qemu_plugin_cond cond,
+ qemu_plugin_u64 entry,
+ uint64_t imm,
+ void *udata);
void plugin_register_vcpu_mem_cb(GArray **arr,
void *cb,
@@ -100,7 +108,9 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
enum qemu_plugin_mem_rw rw,
void *udata);
-void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index);
+void exec_inline_op(enum plugin_dyn_cb_type type,
+ struct qemu_plugin_inline_cb *cb,
+ int cpu_index);
int plugin_num_vcpus(void);
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index a9fac056c7..aa0a77a319 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -27,6 +27,7 @@
qemu_plugin_register_vcpu_idle_cb;
qemu_plugin_register_vcpu_init_cb;
qemu_plugin_register_vcpu_insn_exec_cb;
+ qemu_plugin_register_vcpu_insn_exec_cond_cb;
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu;
qemu_plugin_register_vcpu_mem_cb;
qemu_plugin_register_vcpu_mem_inline_per_vcpu;
@@ -34,6 +35,7 @@
qemu_plugin_register_vcpu_syscall_cb;
qemu_plugin_register_vcpu_syscall_ret_cb;
qemu_plugin_register_vcpu_tb_exec_cb;
+ qemu_plugin_register_vcpu_tb_exec_cond_cb;
qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu;
qemu_plugin_register_vcpu_tb_trans_cb;
qemu_plugin_reset;
diff --git a/qapi/machine-target.json b/qapi/machine-target.json
index 29e695aa06..2942853092 100644
--- a/qapi/machine-target.json
+++ b/qapi/machine-target.json
@@ -20,11 +20,16 @@
#
# @props: a dictionary of QOM properties to be applied
#
+# @deprecated-props: a list of properties that are flagged as deprecated
+# by the CPU vendor. These props are a subset of the full model's
+# definition list of properties. (since 9.1)
+#
# Since: 2.8
##
{ 'struct': 'CpuModelInfo',
'data': { 'name': 'str',
- '*props': 'any' } }
+ '*props': 'any',
+ '*deprecated-props': ['str'] } }
##
# @CpuModelExpansionType:
diff --git a/qapi/meson.build b/qapi/meson.build
index c92af6e063..e7bc54e5d0 100644
--- a/qapi/meson.build
+++ b/qapi/meson.build
@@ -52,6 +52,7 @@ qapi_all_modules = [
'stats',
'trace',
'transaction',
+ 'vfio',
'virtio',
'yank',
]
diff --git a/qapi/migration.json b/qapi/migration.json
index 8c65b90328..a351fd3714 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -23,9 +23,6 @@
#
# @duplicate: number of duplicate (zero) pages (since 1.2)
#
-# @skipped: number of skipped zero pages. Always zero, only provided
-# for compatibility (since 1.5)
-#
# @normal: number of normal pages (since 1.2)
#
# @normal-bytes: number of normal bytes sent (since 1.2)
@@ -63,16 +60,11 @@
# between 0 and @dirty-sync-count * @multifd-channels. (since
# 7.1)
#
-# Features:
-#
-# @deprecated: Member @skipped is always zero since 1.5.3
-#
# Since: 0.14
##
{ 'struct': 'MigrationStats',
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
'duplicate': 'int',
- 'skipped': { 'type': 'int', 'features': [ 'deprecated' ] },
'normal': 'int',
'normal-bytes': 'int', 'dirty-pages-rate': 'int',
'mbps': 'number', 'dirty-sync-count': 'int',
@@ -201,9 +193,6 @@
# @ram: @MigrationStats containing detailed migration status, only
# returned if status is 'active' or 'completed'(since 1.2)
#
-# @disk: @MigrationStats containing detailed disk migration status,
-# only returned if status is 'active' and it is a block migration
-#
# @xbzrle-cache: @XBZRLECacheStats containing detailed XBZRLE
# migration statistics, only returned if XBZRLE feature is on and
# status is 'active' or 'completed' (since 1.2)
@@ -240,10 +229,6 @@
# This is only present when the postcopy-blocktime migration
# capability is enabled. (Since 3.0)
#
-# @compression: migration compression statistics, only returned if
-# compression feature is on and status is 'active' or 'completed'
-# (Since 3.1)
-#
# @socket-address: Only used for tcp, to know what the real port is
# (Since 4.0)
#
@@ -268,19 +253,10 @@
# average memory load of the virtual CPU indirectly. Note that
# zero means guest doesn't dirty memory. (Since 8.1)
#
-# Features:
-#
-# @deprecated: Member @disk is deprecated because block migration is.
-# Member @compression is deprecated because it is unreliable and
-# untested. It is recommended to use multifd migration, which
-# offers an alternative compression implementation that is
-# reliable and tested.
-#
# Since: 0.14
##
{ 'struct': 'MigrationInfo',
'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats',
- '*disk': { 'type': 'MigrationStats', 'features': [ 'deprecated' ] },
'*vfio': 'VfioStats',
'*xbzrle-cache': 'XBZRLECacheStats',
'*total-time': 'int',
@@ -292,7 +268,6 @@
'*blocked-reasons': ['str'],
'*postcopy-blocktime': 'uint32',
'*postcopy-vcpu-blocktime': ['uint32'],
- '*compression': { 'type': 'CompressionStats', 'features': [ 'deprecated' ] },
'*socket-address': ['SocketAddress'],
'*dirty-limit-throttle-time-per-round': 'uint64',
'*dirty-limit-ring-full-time': 'uint64'} }
@@ -302,8 +277,7 @@
#
# Returns information about current migration process. If migration
# is active there will be another json-object with RAM migration
-# status and if block migration is active another one with block
-# migration status.
+# status.
#
# Returns: @MigrationInfo
#
@@ -341,7 +315,7 @@
# -> { "execute": "query-migrate" }
# <- { "return": { "status": "failed" } }
#
-# 4. Migration is being performed and is not a block migration:
+# 4. Migration is being performed:
#
# -> { "execute": "query-migrate" }
# <- {
@@ -362,33 +336,7 @@
# }
# }
#
-# 5. Migration is being performed and is a block migration:
-#
-# -> { "execute": "query-migrate" }
-# <- {
-# "return":{
-# "status":"active",
-# "total-time":12345,
-# "setup-time":12345,
-# "expected-downtime":12345,
-# "ram":{
-# "total":1057024,
-# "remaining":1053304,
-# "transferred":3720,
-# "duplicate":123,
-# "normal":123,
-# "normal-bytes":123456,
-# "dirty-sync-count":15
-# },
-# "disk":{
-# "total":20971520,
-# "remaining":20880384,
-# "transferred":91136
-# }
-# }
-# }
-#
-# 6. Migration is being performed and XBZRLE is active:
+# 5. Migration is being performed and XBZRLE is active:
#
# -> { "execute": "query-migrate" }
# <- {
@@ -441,14 +389,6 @@
# capability on the source VM. The feature is disabled by default.
# (since 1.6)
#
-# @compress: Use multiple compression threads to accelerate live
-# migration. This feature can help to reduce the migration
-# traffic, by sending compressed pages. Please note that if
-# compress and xbzrle are both on, compress only takes effect in
-# the ram bulk stage, after that, it will be disabled and only
-# xbzrle takes effect, this can help to minimize migration
-# traffic. The feature is disabled by default. (since 2.4)
-#
# @events: generate events for each migration state change (since 2.4)
#
# @auto-converge: If enabled, QEMU will automatically throttle down
@@ -468,11 +408,6 @@
# @release-ram: if enabled, qemu will free the migrated ram pages on
# the source during postcopy-ram migration. (since 2.9)
#
-# @block: If enabled, QEMU will also migrate the contents of all block
-# devices. Default is disabled. A possible alternative uses
-# mirror jobs to a builtin NBD server on the destination, which
-# offers more flexibility. (Since 2.10)
-#
# @return-path: If enabled, migration will use the return path even
# for precopy. (since 2.10)
#
@@ -536,23 +471,15 @@
#
# Features:
#
-# @deprecated: Member @block is deprecated. Use blockdev-mirror with
-# NBD instead. Member @compress is deprecated because it is
-# unreliable and untested. It is recommended to use multifd
-# migration, which offers an alternative compression
-# implementation that is reliable and tested.
-#
# @unstable: Members @x-colo and @x-ignore-shared are experimental.
#
# Since: 1.2
##
{ 'enum': 'MigrationCapability',
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
- { 'name': 'compress', 'features': [ 'deprecated' ] },
'events', 'postcopy-ram',
{ 'name': 'x-colo', 'features': [ 'unstable' ] },
'release-ram',
- { 'name': 'block', 'features': [ 'deprecated' ] },
'return-path', 'pause-before-switchover', 'multifd',
'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate',
{ 'name': 'x-ignore-shared', 'features': [ 'unstable' ] },
@@ -609,7 +536,6 @@
# {"state": false, "capability": "rdma-pin-all"},
# {"state": false, "capability": "auto-converge"},
# {"state": false, "capability": "zero-blocks"},
-# {"state": false, "capability": "compress"},
# {"state": true, "capability": "events"},
# {"state": false, "capability": "postcopy-ram"},
# {"state": false, "capability": "x-colo"}
@@ -757,27 +683,6 @@
# @announce-step: Increase in delay (in milliseconds) between
# subsequent packets in the announcement (Since 4.0)
#
-# @compress-level: Set the compression level to be used in live
-# migration, the compression level is an integer between 0 and 9,
-# where 0 means no compression, 1 means the best compression
-# speed, and 9 means best compression ratio which will consume
-# more CPU.
-#
-# @compress-threads: Set compression thread count to be used in live
-# migration, the compression thread count is an integer between 1
-# and 255.
-#
-# @compress-wait-thread: Controls behavior when all compression
-# threads are currently busy. If true (default), wait for a free
-# compression thread to become available; otherwise, send the page
-# uncompressed. (Since 3.1)
-#
-# @decompress-threads: Set decompression thread count to be used in
-# live migration, the decompression thread count is an integer
-# between 1 and 255. Usually, decompression is at least 4 times as
-# fast as compression, so set the decompress-threads to the number
-# about 1/4 of compress-threads is adequate.
-#
# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
# bytes_xfer_period to trigger throttling. It is expressed as
# percentage. The default value is 50. (Since 5.0)
@@ -847,13 +752,6 @@
# @x-checkpoint-delay: The delay time (in ms) between two COLO
# checkpoints in periodic mode. (Since 2.8)
#
-# @block-incremental: Affects how much storage is migrated when the
-# block migration capability is enabled. When false, the entire
-# storage backing chain is migrated into a flattened image at the
-# destination; when true, only the active qcow2 layer is migrated
-# and the destination must already have access to the same backing
-# chain as was used on the source. (since 2.10)
-#
# @multifd-channels: Number of channels used to migrate data in
# parallel. This is the same number that the number of sockets
# used for migration. The default value is 2 (since 4.0)
@@ -916,11 +814,6 @@
#
# Features:
#
-# @deprecated: Member @block-incremental is deprecated. Use
-# blockdev-mirror with NBD instead. Members @compress-level,
-# @compress-threads, @decompress-threads and @compress-wait-thread
-# are deprecated because @compression is deprecated.
-#
# @unstable: Members @x-checkpoint-delay and
# @x-vcpu-dirty-limit-period are experimental.
#
@@ -929,17 +822,12 @@
{ 'enum': 'MigrationParameter',
'data': ['announce-initial', 'announce-max',
'announce-rounds', 'announce-step',
- { 'name': 'compress-level', 'features': [ 'deprecated' ] },
- { 'name': 'compress-threads', 'features': [ 'deprecated' ] },
- { 'name': 'decompress-threads', 'features': [ 'deprecated' ] },
- { 'name': 'compress-wait-thread', 'features': [ 'deprecated' ] },
'throttle-trigger-threshold',
'cpu-throttle-initial', 'cpu-throttle-increment',
'cpu-throttle-tailslow',
'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
'avail-switchover-bandwidth', 'downtime-limit',
{ 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
- { 'name': 'block-incremental', 'features': [ 'deprecated' ] },
'multifd-channels',
'xbzrle-cache-size', 'max-postcopy-bandwidth',
'max-cpu-throttle', 'multifd-compression',
@@ -965,27 +853,6 @@
# @announce-step: Increase in delay (in milliseconds) between
# subsequent packets in the announcement (Since 4.0)
#
-# @compress-level: Set the compression level to be used in live
-# migration, the compression level is an integer between 0 and 9,
-# where 0 means no compression, 1 means the best compression
-# speed, and 9 means best compression ratio which will consume
-# more CPU.
-#
-# @compress-threads: Set compression thread count to be used in live
-# migration, the compression thread count is an integer between 1
-# and 255.
-#
-# @compress-wait-thread: Controls behavior when all compression
-# threads are currently busy. If true (default), wait for a free
-# compression thread to become available; otherwise, send the page
-# uncompressed. (Since 3.1)
-#
-# @decompress-threads: Set decompression thread count to be used in
-# live migration, the decompression thread count is an integer
-# between 1 and 255. Usually, decompression is at least 4 times as
-# fast as compression, so set the decompress-threads to the number
-# about 1/4 of compress-threads is adequate.
-#
# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
# bytes_xfer_period to trigger throttling. It is expressed as
# percentage. The default value is 50. (Since 5.0)
@@ -1055,13 +922,6 @@
# @x-checkpoint-delay: The delay time (in ms) between two COLO
# checkpoints in periodic mode. (Since 2.8)
#
-# @block-incremental: Affects how much storage is migrated when the
-# block migration capability is enabled. When false, the entire
-# storage backing chain is migrated into a flattened image at the
-# destination; when true, only the active qcow2 layer is migrated
-# and the destination must already have access to the same backing
-# chain as was used on the source. (since 2.10)
-#
# @multifd-channels: Number of channels used to migrate data in
# parallel. This is the same number that the number of sockets
# used for migration. The default value is 2 (since 4.0)
@@ -1124,11 +984,6 @@
#
# Features:
#
-# @deprecated: Member @block-incremental is deprecated. Use
-# blockdev-mirror with NBD instead. Members @compress-level,
-# @compress-threads, @decompress-threads and @compress-wait-thread
-# are deprecated because @compression is deprecated.
-#
# @unstable: Members @x-checkpoint-delay and
# @x-vcpu-dirty-limit-period are experimental.
#
@@ -1142,14 +997,6 @@
'*announce-max': 'size',
'*announce-rounds': 'size',
'*announce-step': 'size',
- '*compress-level': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
- '*compress-threads': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
- '*compress-wait-thread': { 'type': 'bool',
- 'features': [ 'deprecated' ] },
- '*decompress-threads': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
'*throttle-trigger-threshold': 'uint8',
'*cpu-throttle-initial': 'uint8',
'*cpu-throttle-increment': 'uint8',
@@ -1162,8 +1009,6 @@
'*downtime-limit': 'uint64',
'*x-checkpoint-delay': { 'type': 'uint32',
'features': [ 'unstable' ] },
- '*block-incremental': { 'type': 'bool',
- 'features': [ 'deprecated' ] },
'*multifd-channels': 'uint8',
'*xbzrle-cache-size': 'size',
'*max-postcopy-bandwidth': 'size',
@@ -1211,17 +1056,6 @@
# @announce-step: Increase in delay (in milliseconds) between
# subsequent packets in the announcement (Since 4.0)
#
-# @compress-level: compression level
-#
-# @compress-threads: compression thread count
-#
-# @compress-wait-thread: Controls behavior when all compression
-# threads are currently busy. If true (default), wait for a free
-# compression thread to become available; otherwise, send the page
-# uncompressed. (Since 3.1)
-#
-# @decompress-threads: decompression thread count
-#
# @throttle-trigger-threshold: The ratio of bytes_dirty_period and
# bytes_xfer_period to trigger throttling. It is expressed as
# percentage. The default value is 50. (Since 5.0)
@@ -1287,13 +1121,6 @@
# @x-checkpoint-delay: the delay time between two COLO checkpoints.
# (Since 2.8)
#
-# @block-incremental: Affects how much storage is migrated when the
-# block migration capability is enabled. When false, the entire
-# storage backing chain is migrated into a flattened image at the
-# destination; when true, only the active qcow2 layer is migrated
-# and the destination must already have access to the same backing
-# chain as was used on the source. (since 2.10)
-#
# @multifd-channels: Number of channels used to migrate data in
# parallel. This is the same number that the number of sockets
# used for migration. The default value is 2 (since 4.0)
@@ -1356,11 +1183,6 @@
#
# Features:
#
-# @deprecated: Member @block-incremental is deprecated. Use
-# blockdev-mirror with NBD instead. Members @compress-level,
-# @compress-threads, @decompress-threads and @compress-wait-thread
-# are deprecated because @compression is deprecated.
-#
# @unstable: Members @x-checkpoint-delay and
# @x-vcpu-dirty-limit-period are experimental.
#
@@ -1371,14 +1193,6 @@
'*announce-max': 'size',
'*announce-rounds': 'size',
'*announce-step': 'size',
- '*compress-level': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
- '*compress-threads': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
- '*compress-wait-thread': { 'type': 'bool',
- 'features': [ 'deprecated' ] },
- '*decompress-threads': { 'type': 'uint8',
- 'features': [ 'deprecated' ] },
'*throttle-trigger-threshold': 'uint8',
'*cpu-throttle-initial': 'uint8',
'*cpu-throttle-increment': 'uint8',
@@ -1391,8 +1205,6 @@
'*downtime-limit': 'uint64',
'*x-checkpoint-delay': { 'type': 'uint32',
'features': [ 'unstable' ] },
- '*block-incremental': { 'type': 'bool',
- 'features': [ 'deprecated' ] },
'*multifd-channels': 'uint8',
'*xbzrle-cache-size': 'size',
'*max-postcopy-bandwidth': 'size',
@@ -1742,20 +1554,11 @@
# @channels: list of migration stream channels with each stream in the
# list connected to a destination interface endpoint.
#
-# @blk: do block migration (full disk copy)
-#
-# @inc: incremental disk copy migration
-#
# @detach: this argument exists only for compatibility reasons and is
# ignored by QEMU
#
# @resume: resume one paused migration, default "off". (since 3.0)
#
-# Features:
-#
-# @deprecated: Members @inc and @blk are deprecated. Use
-# blockdev-mirror with NBD instead.
-#
# Since: 0.14
#
# Notes:
@@ -1821,8 +1624,6 @@
{ 'command': 'migrate',
'data': {'*uri': 'str',
'*channels': [ 'MigrationChannel' ],
- '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] },
- '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] },
'*detach': 'bool', '*resume': 'bool' } }
##
@@ -1837,6 +1638,10 @@
# @channels: list of migration stream channels with each stream in the
# list connected to a destination interface endpoint.
#
+# @exit-on-error: Exit on incoming migration failure. Default true.
+# When set to false, the failure triggers a MIGRATION event, and
+# error details could be retrieved with query-migrate. (since 9.1)
+#
# Since: 2.3
#
# Notes:
@@ -1889,7 +1694,8 @@
##
{ 'command': 'migrate-incoming',
'data': {'*uri': 'str',
- '*channels': [ 'MigrationChannel' ] } }
+ '*channels': [ 'MigrationChannel' ],
+ '*exit-on-error': 'bool' } }
##
# @xen-save-devices-state:
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index 5e33da7228..b1581988e4 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -78,5 +78,6 @@
{ 'include': 'pci.json' }
{ 'include': 'stats.json' }
{ 'include': 'virtio.json' }
+{ 'include': 'vfio.json' }
{ 'include': 'cryptodev.json' }
{ 'include': 'cxl.json' }
diff --git a/qapi/vfio.json b/qapi/vfio.json
new file mode 100644
index 0000000000..a0e5013188
--- /dev/null
+++ b/qapi/vfio.json
@@ -0,0 +1,67 @@
+# -*- Mode: Python -*-
+# vim: filetype=python
+#
+
+##
+# = VFIO devices
+##
+
+##
+# @VfioMigrationState:
+#
+# An enumeration of the VFIO device migration states.
+#
+# @stop: The device is stopped.
+#
+# @running: The device is running.
+#
+# @stop-copy: The device is stopped and its internal state is available
+# for reading.
+#
+# @resuming: The device is stopped and its internal state is available
+# for writing.
+#
+# @running-p2p: The device is running in the P2P quiescent state.
+#
+# @pre-copy: The device is running, tracking its internal state and its
+# internal state is available for reading.
+#
+# @pre-copy-p2p: The device is running in the P2P quiescent state,
+# tracking its internal state and its internal state is available
+# for reading.
+#
+# Since: 9.1
+##
+{ 'enum': 'VfioMigrationState',
+ 'data': [ 'stop', 'running', 'stop-copy', 'resuming', 'running-p2p',
+ 'pre-copy', 'pre-copy-p2p' ],
+ 'prefix': 'QAPI_VFIO_MIGRATION_STATE' }
+
+##
+# @VFIO_MIGRATION:
+#
+# This event is emitted when a VFIO device migration state is changed.
+#
+# @device-id: The device's id, if it has one.
+#
+# @qom-path: The device's QOM path.
+#
+# @device-state: The new changed device migration state.
+#
+# Since: 9.1
+#
+# Example:
+#
+# <- { "timestamp": { "seconds": 1713771323, "microseconds": 212268 },
+# "event": "VFIO_MIGRATION",
+# "data": {
+# "device-id": "vfio_dev1",
+# "qom-path": "/machine/peripheral/vfio_dev1",
+# "device-state": "stop" } }
+##
+{ 'event': 'VFIO_MIGRATION',
+ 'data': {
+ 'device-id': 'str',
+ 'qom-path': 'str',
+ 'device-state': 'VfioMigrationState'
+ } }
diff --git a/qemu-options.hx b/qemu-options.hx
index cf61f6b863..4d19660336 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2516,6 +2516,10 @@ SRST
host. It is possible to control the websocket listen address
independently, using the syntax ``websocket``\ =host:port.
+ Websocket could be allowed over UNIX domain socket, using the syntax
+ ``websocket``\ =unix:path, where path is the location of a unix socket
+ to listen for connections on.
+
If no TLS credentials are provided, the websocket connection
runs in unencrypted mode. If TLS credentials are provided, the
websocket connection requires encrypted client connections.
@@ -4824,7 +4828,8 @@ DEF("runas", HAS_ARG, QEMU_OPTION_runas, \
SRST
``-runas user``
Immediately before starting guest execution, drop root privileges,
- switching to the specified user.
+ switching to the specified user. This option is deprecated, use
+ ``-run-with user=...`` instead.
ERST
DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env,
@@ -4990,13 +4995,15 @@ DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
#ifdef CONFIG_POSIX
DEF("run-with", HAS_ARG, QEMU_OPTION_run_with,
- "-run-with [async-teardown=on|off][,chroot=dir]\n"
+ "-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\n"
" Set miscellaneous QEMU process lifecycle options:\n"
" async-teardown=on enables asynchronous teardown (Linux only)\n"
- " chroot=dir chroot to dir just before starting the VM\n",
+ " chroot=dir chroot to dir just before starting the VM\n"
+ " user=username switch to the specified user before starting the VM\n"
+ " user=uid:gid ditto, but use specified user-ID and group-ID instead\n",
QEMU_ARCH_ALL)
SRST
-``-run-with [async-teardown=on|off][,chroot=dir]``
+``-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]``
Set QEMU process lifecycle options.
``async-teardown=on`` enables asynchronous teardown. A new process called
@@ -5013,6 +5020,10 @@ SRST
``chroot=dir`` can be used for doing a chroot to the specified directory
immediately before starting the guest execution. This is especially useful
in combination with -runas.
+
+ ``user=username`` or ``user=uid:gid`` can be used to drop root privileges
+ by switching to the specified user (via username) or user and group
+ (via uid:gid) immediately before starting guest execution.
ERST
#endif
diff --git a/qga/commands-common-ssh.c b/qga/commands-common-ssh.c
new file mode 100644
index 0000000000..537869fb98
--- /dev/null
+++ b/qga/commands-common-ssh.c
@@ -0,0 +1,50 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "commands-common-ssh.h"
+
+GStrv read_authkeys(const char *path, Error **errp)
+{
+ g_autoptr(GError) err = NULL;
+ g_autofree char *contents = NULL;
+
+ if (!g_file_get_contents(path, &contents, NULL, &err)) {
+ error_setg(errp, "failed to read '%s': %s", path, err->message);
+ return NULL;
+ }
+
+ return g_strsplit(contents, "\n", -1);
+}
+
+bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
+{
+ size_t n = 0;
+ strList *k;
+
+ for (k = keys; k != NULL; k = k->next) {
+ if (!check_openssh_pub_key(k->value, errp)) {
+ return false;
+ }
+ n++;
+ }
+
+ if (nkeys) {
+ *nkeys = n;
+ }
+ return true;
+}
+
+bool check_openssh_pub_key(const char *key, Error **errp)
+{
+ /* simple sanity-check, we may want more? */
+ if (!key || key[0] == '#' || strchr(key, '\n')) {
+ error_setg(errp, "invalid OpenSSH public key: '%s'", key);
+ return false;
+ }
+
+ return true;
+}
diff --git a/qga/commands-common-ssh.h b/qga/commands-common-ssh.h
new file mode 100644
index 0000000000..14d955fa84
--- /dev/null
+++ b/qga/commands-common-ssh.h
@@ -0,0 +1,10 @@
+/*
+ * 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 "qapi/qapi-builtin-types.h"
+
+GStrv read_authkeys(const char *path, Error **errp);
+bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp);
+bool check_openssh_pub_key(const char *key, Error **errp);
diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c
index 236f80de44..246171d323 100644
--- a/qga/commands-posix-ssh.c
+++ b/qga/commands-posix-ssh.c
@@ -9,6 +9,7 @@
#include <locale.h>
#include <pwd.h>
+#include "commands-common-ssh.h"
#include "qapi/error.h"
#include "qga-qapi-commands.h"
@@ -35,7 +36,7 @@ test_get_passwd_entry(const gchar *user_name, GError **error)
return p;
}
-#define g_unix_get_passwd_entry_qemu(username, err) \
+#define g_unix_get_passwd_entry(username, err) \
test_get_passwd_entry(username, err)
#endif
@@ -45,7 +46,7 @@ get_passwd_entry(const char *username, Error **errp)
g_autoptr(GError) err = NULL;
struct passwd *p;
- p = g_unix_get_passwd_entry_qemu(username, &err);
+ p = g_unix_get_passwd_entry(username, &err);
if (p == NULL) {
error_setg(errp, "failed to lookup user '%s': %s",
username, err->message);
@@ -81,37 +82,6 @@ mkdir_for_user(const char *path, const struct passwd *p,
}
static bool
-check_openssh_pub_key(const char *key, Error **errp)
-{
- /* simple sanity-check, we may want more? */
- if (!key || key[0] == '#' || strchr(key, '\n')) {
- error_setg(errp, "invalid OpenSSH public key: '%s'", key);
- return false;
- }
-
- return true;
-}
-
-static bool
-check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
-{
- size_t n = 0;
- strList *k;
-
- for (k = keys; k != NULL; k = k->next) {
- if (!check_openssh_pub_key(k->value, errp)) {
- return false;
- }
- n++;
- }
-
- if (nkeys) {
- *nkeys = n;
- }
- return true;
-}
-
-static bool
write_authkeys(const char *path, const GStrv keys,
const struct passwd *p, Error **errp)
{
@@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys,
return true;
}
-static GStrv
-read_authkeys(const char *path, Error **errp)
-{
- g_autoptr(GError) err = NULL;
- g_autofree char *contents = NULL;
-
- if (!g_file_get_contents(path, &contents, NULL, &err)) {
- error_setg(errp, "failed to read '%s': %s", path, err->message);
- return NULL;
- }
-
- return g_strsplit(contents, "\n", -1);
-
-}
-
void
qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
bool has_reset, bool reset,
@@ -288,7 +243,6 @@ qmp_guest_ssh_get_authorized_keys(const char *username, Error **errp)
}
#ifdef QGA_BUILD_UNIT_TEST
-#if GLIB_CHECK_VERSION(2, 60, 0)
static const strList test_key2 = {
.value = (char *)"algo key2 comments"
};
@@ -484,11 +438,4 @@ int main(int argc, char *argv[])
return g_test_run();
}
-#else
-int main(int argc, char *argv[])
-{
- g_test_message("test skipped, needs glib >= 2.60");
- return 0;
-}
-#endif /* GLIB_2_60 */
#endif /* BUILD_UNIT_TEST */
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 26008db497..7f05996495 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -76,12 +76,159 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp)
g_assert(rpid == pid);
}
+static ssize_t ga_pipe_read_str(int fd[2], char **str)
+{
+ ssize_t n, len = 0;
+ char buf[1024];
+
+ close(fd[1]);
+ fd[1] = -1;
+ while ((n = read(fd[0], buf, sizeof(buf))) != 0) {
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ len = -errno;
+ break;
+ }
+ }
+ *str = g_realloc(*str, len + n + 1);
+ memcpy(*str + len, buf, n);
+ len += n;
+ *str[len] = '\0';
+ }
+ close(fd[0]);
+ fd[0] = -1;
+
+ return len;
+}
+
+/*
+ * Helper to run command with input/output redirection,
+ * sending string to stdin and taking error message from
+ * stdout/err.
+ */
+static int ga_run_command(const char *argv[], const char *in_str,
+ const char *action, Error **errp)
+{
+ pid_t pid;
+ int status;
+ int retcode = -1;
+ int infd[2] = { -1, -1 };
+ int outfd[2] = { -1, -1 };
+ char *str = NULL;
+ ssize_t len = 0;
+
+ if ((in_str && !g_unix_open_pipe(infd, FD_CLOEXEC, NULL)) ||
+ !g_unix_open_pipe(outfd, FD_CLOEXEC, NULL)) {
+ error_setg(errp, "cannot create pipe FDs");
+ goto out;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ char *cherr = NULL;
+
+ setsid();
+
+ if (in_str) {
+ /* Redirect stdin to infd. */
+ close(infd[1]);
+ dup2(infd[0], 0);
+ close(infd[0]);
+ } else {
+ reopen_fd_to_null(0);
+ }
+
+ /* Redirect stdout/stderr to outfd. */
+ close(outfd[0]);
+ dup2(outfd[1], 1);
+ dup2(outfd[1], 2);
+ close(outfd[1]);
+
+ execvp(argv[0], (char *const *)argv);
+
+ /* Write the cause of failed exec to pipe for the parent to read it. */
+ cherr = g_strdup_printf("failed to exec '%s'", argv[0]);
+ perror(cherr);
+ g_free(cherr);
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(errp, errno, "failed to create child process");
+ goto out;
+ }
+
+ if (in_str) {
+ close(infd[0]);
+ infd[0] = -1;
+ if (qemu_write_full(infd[1], in_str, strlen(in_str)) !=
+ strlen(in_str)) {
+ error_setg_errno(errp, errno, "%s: cannot write to stdin pipe",
+ action);
+ goto out;
+ }
+ close(infd[1]);
+ infd[1] = -1;
+ }
+
+ len = ga_pipe_read_str(outfd, &str);
+ if (len < 0) {
+ error_setg_errno(errp, -len, "%s: cannot read from stdout/stderr pipe",
+ action);
+ goto out;
+ }
+
+ ga_wait_child(pid, &status, errp);
+ if (*errp) {
+ goto out;
+ }
+
+ if (!WIFEXITED(status)) {
+ if (len) {
+ error_setg(errp, "child process has terminated abnormally: %s",
+ str);
+ } else {
+ error_setg(errp, "child process has terminated abnormally");
+ }
+ goto out;
+ }
+
+ retcode = WEXITSTATUS(status);
+
+ if (WEXITSTATUS(status)) {
+ if (len) {
+ error_setg(errp, "child process has failed to %s: %s",
+ action, str);
+ } else {
+ error_setg(errp, "child process has failed to %s: exit status %d",
+ action, WEXITSTATUS(status));
+ }
+ goto out;
+ }
+
+out:
+ g_free(str);
+
+ if (infd[0] != -1) {
+ close(infd[0]);
+ }
+ if (infd[1] != -1) {
+ close(infd[1]);
+ }
+ if (outfd[0] != -1) {
+ close(outfd[0]);
+ }
+ if (outfd[1] != -1) {
+ close(outfd[1]);
+ }
+
+ return retcode;
+}
+
void qmp_guest_shutdown(const char *mode, Error **errp)
{
const char *shutdown_flag;
Error *local_err = NULL;
- pid_t pid;
- int status;
#ifdef CONFIG_SOLARIS
const char *powerdown_flag = "-i5";
@@ -110,67 +257,31 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
return;
}
- pid = fork();
- if (pid == 0) {
- /* child, start the shutdown */
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
+ const char *argv[] = {"/sbin/shutdown",
#ifdef CONFIG_SOLARIS
- execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
- "hypervisor initiated shutdown", (char *)NULL);
+ shutdown_flag, "-g0", "-y",
#elif defined(CONFIG_BSD)
- execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char *)NULL);
+ shutdown_flag, "+0",
#else
- execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char *)NULL);
+ "-h", shutdown_flag, "+0",
#endif
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
+ "hypervisor initiated shutdown", (char *) NULL};
- ga_wait_child(pid, &status, &local_err);
+ ga_run_command(argv, NULL, "shutdown", &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to shutdown");
- return;
- }
-
/* succeeded */
}
void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
{
int ret;
- int status;
- pid_t pid;
Error *local_err = NULL;
struct timeval tv;
- static const char hwclock_path[] = "/sbin/hwclock";
- static int hwclock_available = -1;
-
- if (hwclock_available < 0) {
- hwclock_available = (access(hwclock_path, X_OK) == 0);
- }
-
- if (!hwclock_available) {
- error_setg(errp, QERR_UNSUPPORTED);
- return;
- }
+ const char *argv[] = {"/sbin/hwclock", has_time ? "-w" : "-s", NULL};
/* If user has passed a time, validate and set it. */
if (has_time) {
@@ -201,37 +312,12 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
* just need to synchronize the hardware clock. However, if no time was
* passed, user is requesting the opposite: set the system time from the
* hardware clock (RTC). */
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- /* Use '/sbin/hwclock -w' to set RTC from the system time,
- * or '/sbin/hwclock -s' to set the system time from RTC. */
- execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
+ ga_run_command(argv, NULL, "set hardware clock to system time",
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "hwclock failed to set hardware clock to system time");
- return;
- }
}
typedef enum {
@@ -650,8 +736,6 @@ static const char *fsfreeze_hook_arg_string[] = {
static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
{
- int status;
- pid_t pid;
const char *hook;
const char *arg_str = fsfreeze_hook_arg_string[arg];
Error *local_err = NULL;
@@ -660,42 +744,15 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
if (!hook) {
return;
}
- if (access(hook, X_OK) != 0) {
- error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
- return;
- }
-
- slog("executing fsfreeze hook with arg '%s'", arg_str);
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
- execl(hook, hook, arg_str, NULL);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
+ const char *argv[] = {hook, arg_str, NULL};
- ga_wait_child(pid, &status, &local_err);
+ slog("executing fsfreeze hook with arg '%s'", arg_str);
+ ga_run_command(argv, NULL, "execute fsfreeze hook", &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "fsfreeze hook has terminated abnormally");
- return;
- }
-
- status = WEXITSTATUS(status);
- if (status) {
- error_setg(errp, "fsfreeze hook has failed with status %d", status);
- return;
- }
}
/*
@@ -1569,8 +1626,10 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
nonroot_total = used + buf.f_bavail;
fs->used_bytes = used * fr_size;
fs->total_bytes = nonroot_total * fr_size;
+ fs->total_bytes_privileged = buf.f_blocks * fr_size;
fs->has_total_bytes = true;
+ fs->has_total_bytes_privileged = true;
fs->has_used_bytes = true;
}
@@ -1869,52 +1928,21 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
{
- Error *local_err = NULL;
+ g_autoptr(GError) local_gerr = NULL;
const char *sysfile_strs[3] = {"disk", "mem", NULL};
const char *sysfile_str = sysfile_strs[mode];
- pid_t pid;
- int status;
if (!sysfile_str) {
error_setg(errp, "unknown guest suspend mode");
return;
}
- pid = fork();
- if (!pid) {
- /* child */
- int fd;
-
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
- if (fd < 0) {
- _exit(EXIT_FAILURE);
- }
-
- if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
+ -1, &local_gerr)) {
+ error_setg(errp, "suspend: cannot write to '%s': %s",
+ LINUX_SYS_STATE_FILE, local_gerr->message);
return;
}
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to suspend");
- }
-
}
static void guest_suspend(SuspendMode mode, Error **errp)
@@ -2123,14 +2151,8 @@ void qmp_guest_set_user_password(const char *username,
Error **errp)
{
Error *local_err = NULL;
- char *passwd_path = NULL;
- pid_t pid;
- int status;
- int datafd[2] = { -1, -1 };
- char *rawpasswddata = NULL;
+ g_autofree char *rawpasswddata = NULL;
size_t rawpasswdlen;
- char *chpasswddata = NULL;
- size_t chpasswdlen;
rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
if (!rawpasswddata) {
@@ -2141,95 +2163,31 @@ void qmp_guest_set_user_password(const char *username,
if (strchr(rawpasswddata, '\n')) {
error_setg(errp, "forbidden characters in raw password");
- goto out;
+ return;
}
if (strchr(username, '\n') ||
strchr(username, ':')) {
error_setg(errp, "forbidden characters in username");
- goto out;
- }
-
-#ifdef __FreeBSD__
- chpasswddata = g_strdup(rawpasswddata);
- passwd_path = g_find_program_in_path("pw");
-#else
- chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
- passwd_path = g_find_program_in_path("chpasswd");
-#endif
-
- chpasswdlen = strlen(chpasswddata);
-
- if (!passwd_path) {
- error_setg(errp, "cannot find 'passwd' program in PATH");
- goto out;
- }
-
- if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
- error_setg(errp, "cannot create pipe FDs");
- goto out;
+ return;
}
- pid = fork();
- if (pid == 0) {
- close(datafd[1]);
- /* child */
- setsid();
- dup2(datafd[0], 0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
#ifdef __FreeBSD__
- const char *h_arg;
- h_arg = (crypted) ? "-H" : "-h";
- execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
+ g_autofree char *chpasswddata = g_strdup(rawpasswddata);
+ const char *crypt_flag = crypted ? "-H" : "-h";
+ const char *argv[] = {"pw", "usermod", "-n", username,
+ crypt_flag, "0", NULL};
#else
- if (crypted) {
- execl(passwd_path, "chpasswd", "-e", NULL);
- } else {
- execl(passwd_path, "chpasswd", NULL);
- }
+ g_autofree char *chpasswddata = g_strdup_printf("%s:%s\n", username,
+ rawpasswddata);
+ const char *crypt_flag = crypted ? "-e" : NULL;
+ const char *argv[] = {"chpasswd", crypt_flag, NULL};
#endif
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
- }
- close(datafd[0]);
- datafd[0] = -1;
-
- if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
- error_setg_errno(errp, errno, "cannot write new account password");
- goto out;
- }
- close(datafd[1]);
- datafd[1] = -1;
- ga_wait_child(pid, &status, &local_err);
+ ga_run_command(argv, chpasswddata, "set user password", &local_err);
if (local_err) {
error_propagate(errp, local_err);
- goto out;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to set user password");
- goto out;
- }
-
-out:
- g_free(chpasswddata);
- g_free(rawpasswddata);
- g_free(passwd_path);
- if (datafd[0] != -1) {
- close(datafd[0]);
- }
- if (datafd[1] != -1) {
- close(datafd[1]);
+ return;
}
}
#else /* __linux__ || __FreeBSD__ */
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 6242737b00..6fee0e1e6f 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1143,6 +1143,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
fs = g_malloc(sizeof(*fs));
fs->name = g_strdup(guid);
fs->has_total_bytes = false;
+ fs->has_total_bytes_privileged = false;
fs->has_used_bytes = false;
if (len == 0) {
fs->mountpoint = g_strdup("System Reserved");
diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c
new file mode 100644
index 0000000000..6a642e3ba8
--- /dev/null
+++ b/qga/commands-windows-ssh.c
@@ -0,0 +1,712 @@
+/*
+ * QEMU Guest Agent win32-specific command implementations for SSH keys.
+ * The implementation is opinionated and expects the SSH implementation to
+ * be OpenSSH.
+ *
+ * Copyright Schweitzer Engineering Laboratories. 2024
+ *
+ * Authors:
+ * Aidan Leuck <aidan_leuck@selinc.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <aclapi.h>
+#include <qga-qapi-types.h>
+
+#include "commands-common-ssh.h"
+#include "commands-windows-ssh.h"
+#include "guest-agent-core.h"
+#include "limits.h"
+#include "lmaccess.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "qapi/error.h"
+
+#include "qga-qapi-commands.h"
+#include "sddl.h"
+#include "shlobj.h"
+#include "userenv.h"
+
+#define AUTHORIZED_KEY_FILE "authorized_keys"
+#define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys"
+#define LOCAL_SYSTEM_SID "S-1-5-18"
+#define ADMIN_SID "S-1-5-32-544"
+
+/*
+ * Frees userInfo structure. This implements the g_auto cleanup
+ * for the structure.
+ */
+void free_userInfo(PWindowsUserInfo info)
+{
+ g_free(info->sshDirectory);
+ g_free(info->authorizedKeyFile);
+ LocalFree(info->SSID);
+ g_free(info->username);
+ g_free(info);
+}
+
+/*
+ * Gets the admin SSH folder for OpenSSH. OpenSSH does not store
+ * the authorized_key file in the users home directory for security reasons and
+ * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to
+ * that directory on the users machine
+ *
+ * parameters:
+ * errp -> error structure to set when an error occurs
+ * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error
+ * occurred.
+ */
+static char *get_admin_ssh_folder(Error **errp)
+{
+ /* Allocate memory for the program data path */
+ g_autofree char *programDataPath = NULL;
+ char *authkeys_path = NULL;
+ PWSTR pgDataW = NULL;
+ g_autoptr(GError) gerr = NULL;
+
+ /* Get the KnownFolderPath on the machine. */
+ HRESULT folderResult =
+ SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW);
+ if (folderResult != S_OK) {
+ error_setg(errp, "Failed to retrieve ProgramData folder");
+ return NULL;
+ }
+
+ /* Convert from a wide string back to a standard character string. */
+ programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr);
+ CoTaskMemFree(pgDataW);
+ if (!programDataPath) {
+ error_setg(errp,
+ "Failed converting ProgramData folder path to UTF-16 %s",
+ gerr->message);
+ return NULL;
+ }
+
+ /* Build the path to the file. */
+ authkeys_path = g_build_filename(programDataPath, "ssh", NULL);
+ return authkeys_path;
+}
+
+/*
+ * Gets the path to the SSH folder for the specified user. If the user is an
+ * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is
+ * not an admin it returns %USERPROFILE%/.ssh
+ *
+ * parameters:
+ * username -> Username to get the SSH folder for
+ * isAdmin -> Whether the user is an admin or not
+ * errp -> Error structure to set any errors that occur.
+ * returns: path to the ssh folder as a string.
+ */
+static char *get_ssh_folder(const char *username, const bool isAdmin,
+ Error **errp)
+{
+ DWORD maxSize = MAX_PATH;
+ g_autofree char *profilesDir = g_new0(char, maxSize);
+
+ if (isAdmin) {
+ return get_admin_ssh_folder(errp);
+ }
+
+ /* If not an Admin the SSH key is in the user directory. */
+ /* Get the user profile directory on the machine. */
+ BOOL ret = GetProfilesDirectory(profilesDir, &maxSize);
+ if (!ret) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to retrieve profiles directory");
+ return NULL;
+ }
+
+ /* Builds the filename */
+ return g_build_filename(profilesDir, username, ".ssh", NULL);
+}
+
+/*
+ * Creates an entry for the user so they can access the ssh folder in their
+ * userprofile.
+ *
+ * parameters:
+ * userInfo -> Information about the current user
+ * pACL -> Pointer to an ACL structure
+ * errp -> Error structure to set any errors that occur
+ * returns -> 1 on success, 0 otherwise
+ */
+static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
+{
+ const int aclSize = 1;
+ PACL newACL = NULL;
+ EXPLICIT_ACCESS eAccess[1];
+ PSID userPSID = NULL;
+
+ /* Get a pointer to the internal SID object in Windows */
+ bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
+ if (!converted) {
+ error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
+ userInfo->username);
+ goto error;
+ }
+
+ /* Set the permissions for the user. */
+ eAccess[0].grfAccessPermissions = GENERIC_ALL;
+ eAccess[0].grfAccessMode = SET_ACCESS;
+ eAccess[0].grfInheritance = NO_INHERITANCE;
+ eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
+ eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID;
+
+ /* Set the ACL entries */
+ DWORD setResult;
+
+ /*
+ * If we are given a pointer that is already initialized, then we can merge
+ * the existing entries instead of overwriting them.
+ */
+ if (*pACL) {
+ setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL);
+ } else {
+ setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL);
+ }
+
+ if (setResult != ERROR_SUCCESS) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to set ACL entries for user %s %lu",
+ userInfo->username, setResult);
+ goto error;
+ }
+
+ /* Free any old memory since we are going to overwrite the users pointer. */
+ LocalFree(*pACL);
+ *pACL = newACL;
+
+ LocalFree(userPSID);
+ return true;
+error:
+ LocalFree(userPSID);
+ return false;
+}
+
+/*
+ * Creates a base ACL for both normal users and admins to share
+ * pACL -> Pointer to an ACL structure
+ * errp -> Error structure to set any errors that occur
+ * returns: 1 on success, 0 otherwise
+ */
+static bool create_acl_base(PACL *pACL, Error **errp)
+{
+ PSID adminGroupPSID = NULL;
+ PSID systemPSID = NULL;
+
+ const int aclSize = 2;
+ EXPLICIT_ACCESS eAccess[2];
+
+ /* Create an entry for the system user. */
+ const char *systemSID = LOCAL_SYSTEM_SID;
+ bool converted = ConvertStringSidToSid(systemSID, &systemPSID);
+ if (!converted) {
+ error_setg_win32(errp, GetLastError(), "failed to retrieve system SID");
+ goto error;
+ }
+
+ /* set permissions for system user */
+ eAccess[0].grfAccessPermissions = GENERIC_ALL;
+ eAccess[0].grfAccessMode = SET_ACCESS;
+ eAccess[0].grfInheritance = NO_INHERITANCE;
+ eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
+ eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID;
+
+ /* Create an entry for the admin user. */
+ const char *adminSID = ADMIN_SID;
+ converted = ConvertStringSidToSid(adminSID, &adminGroupPSID);
+ if (!converted) {
+ error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID");
+ goto error;
+ }
+
+ /* Set permissions for admin group. */
+ eAccess[1].grfAccessPermissions = GENERIC_ALL;
+ eAccess[1].grfAccessMode = SET_ACCESS;
+ eAccess[1].grfInheritance = NO_INHERITANCE;
+ eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
+ eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID;
+
+ /* Put the entries in an ACL object. */
+ PACL pNewACL = NULL;
+ DWORD setResult;
+
+ /*
+ *If we are given a pointer that is already initialized, then we can merge
+ *the existing entries instead of overwriting them.
+ */
+ if (*pACL) {
+ setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL);
+ } else {
+ setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL);
+ }
+
+ if (setResult != ERROR_SUCCESS) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to set base ACL entries for system user and "
+ "admin group %lu",
+ setResult);
+ goto error;
+ }
+
+ LocalFree(adminGroupPSID);
+ LocalFree(systemPSID);
+
+ /* Free any old memory since we are going to overwrite the users pointer. */
+ LocalFree(*pACL);
+
+ *pACL = pNewACL;
+
+ return true;
+
+error:
+ LocalFree(adminGroupPSID);
+ LocalFree(systemPSID);
+ return false;
+}
+
+/*
+ * Sets the access control on the authorized_keys file and any ssh folders that
+ * need to be created. For administrators the required permissions on the
+ * file/folders are that only administrators and the LocalSystem account can
+ * access the folders. For normal user accounts only the specified user,
+ * LocalSystem and Administrators can have access to the key.
+ *
+ * parameters:
+ * userInfo -> pointer to structure that contains information about the user
+ * PACL -> pointer to an access control structure that will be set upon
+ * successful completion of the function.
+ * errp -> error structure that will be set upon error.
+ * returns: 1 upon success 0 upon failure.
+ */
+static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp)
+{
+ /*
+ * Creates a base ACL that both admins and users will share
+ * This adds the Administrators group and the SYSTEM group
+ */
+ if (!create_acl_base(pACL, errp)) {
+ return false;
+ }
+
+ /*
+ * If the user is not an admin give the user creating the key permission to
+ * access the file.
+ */
+ if (!userInfo->isAdmin) {
+ if (!create_acl_user(userInfo, pACL, errp)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ return true;
+}
+/*
+ * Create the SSH directory for the user and d sets appropriate permissions.
+ * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin.
+ * %USERPOFILE%/.ssh if not an admin
+ *
+ * parameters:
+ * userInfo -> Contains information about the user
+ * errp -> Structure that will contain errors if the function fails.
+ * returns: zero upon failure, 1 upon success
+ */
+static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp)
+{
+ PACL pNewACL = NULL;
+ g_autofree PSECURITY_DESCRIPTOR pSD = NULL;
+
+ /* Gets the appropriate ACL for the user */
+ if (!create_acl(userInfo, &pNewACL, errp)) {
+ goto error;
+ }
+
+ /* Allocate memory for a security descriptor */
+ pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+ error_setg_win32(errp, GetLastError(),
+ "Failed to initialize security descriptor");
+ goto error;
+ }
+
+ /* Associate the security descriptor with the ACL permissions. */
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) {
+ error_setg_win32(errp, GetLastError(),
+ "Failed to set security descriptor ACL");
+ goto error;
+ }
+
+ /* Set the security attributes on the folder */
+ SECURITY_ATTRIBUTES sAttr;
+ sAttr.bInheritHandle = FALSE;
+ sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sAttr.lpSecurityDescriptor = pSD;
+
+ /* Create the directory with the created permissions */
+ BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr);
+ if (!created) {
+ error_setg_win32(errp, GetLastError(), "failed to create directory %s",
+ userInfo->sshDirectory);
+ goto error;
+ }
+
+ /* Free memory */
+ LocalFree(pNewACL);
+ return true;
+error:
+ LocalFree(pNewACL);
+ return false;
+}
+
+/*
+ * Sets permissions on the authorized_key_file that is created.
+ *
+ * parameters: userInfo -> Information about the user
+ * errp -> error structure that will contain errors upon failure
+ * returns: 1 upon success, zero upon failure.
+ */
+static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp)
+{
+ PACL pACL = NULL;
+ PSID userPSID;
+
+ /* Creates the access control structure */
+ if (!create_acl(userInfo, &pACL, errp)) {
+ goto error;
+ }
+
+ /* Get the PSID structure for the user based off the string SID. */
+ bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID);
+ if (!converted) {
+ error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID",
+ userInfo->username);
+ goto error;
+ }
+
+ /* Prevents permissions from being inherited and use the DACL provided. */
+ const SE_OBJECT_TYPE securityBitFlags =
+ DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
+
+ /* Set the ACL on the file. */
+ if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT,
+ securityBitFlags, userPSID, NULL, pACL,
+ NULL) != ERROR_SUCCESS) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to set file security for file %s",
+ userInfo->authorizedKeyFile);
+ goto error;
+ }
+
+ LocalFree(pACL);
+ LocalFree(userPSID);
+ return true;
+
+error:
+ LocalFree(pACL);
+ LocalFree(userPSID);
+
+ return false;
+}
+
+/*
+ * Writes the specified keys to the authenticated keys file.
+ * parameters:
+ * userInfo: Information about the user we are writing the authkeys file to.
+ * authkeys: Array of keys to write to disk
+ * errp: Error structure that will contain any errors if they occur.
+ * returns: 1 if successful, 0 otherwise.
+ */
+static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys,
+ Error **errp)
+{
+ g_autofree char *contents = NULL;
+ g_autoptr(GError) err = NULL;
+
+ contents = g_strjoinv("\n", authkeys);
+
+ if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) {
+ error_setg(errp, "failed to write to '%s': %s",
+ userInfo->authorizedKeyFile, err->message);
+ return false;
+ }
+
+ if (!set_file_permissions(userInfo, errp)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Retrieves information about a Windows user by their username
+ *
+ * parameters:
+ * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it
+ * will be allocated with information about the user and need to be freed.
+ * username -> Name of the user to lookup.
+ * errp -> Contains any errors that occur.
+ * returns: 1 upon success, 0 upon failure.
+ */
+static bool get_user_info(PWindowsUserInfo *userInfo, const char *username,
+ Error **errp)
+{
+ DWORD infoLevel = 4;
+ LPUSER_INFO_4 uBuf = NULL;
+ g_autofree wchar_t *wideUserName = NULL;
+ g_autoptr(GError) gerr = NULL;
+ PSID psid = NULL;
+
+ /*
+ * Converts a string to a Windows wide string since the GetNetUserInfo
+ * function requires it.
+ */
+ wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr);
+ if (!wideUserName) {
+ goto error;
+ }
+
+ /* allocate data */
+ PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1);
+
+ /* Set pointer so it can be cleaned up by the callee, even upon error. */
+ *userInfo = uData;
+
+ /* Find the information */
+ NET_API_STATUS result =
+ NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf);
+ if (result != NERR_Success) {
+ /* Give a friendlier error message if the user was not found. */
+ if (result == NERR_UserNotFound) {
+ error_setg(errp, "User %s was not found", username);
+ goto error;
+ }
+
+ error_setg(errp,
+ "Received unexpected error when asking for user info: Error "
+ "Code %lu",
+ result);
+ goto error;
+ }
+
+ /* Get information from the buffer returned by NetUserGetInfo. */
+ uData->username = g_strdup(username);
+ uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN;
+ psid = uBuf->usri4_user_sid;
+
+ char *sidStr = NULL;
+
+ /*
+ * We store the string representation of the SID not SID structure in
+ * memory. Callees wanting to use the SID structure should call
+ * ConvertStringSidToSID.
+ */
+ if (!ConvertSidToStringSid(psid, &sidStr)) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get SID string for user %s", username);
+ goto error;
+ }
+
+ /* Store the SSID */
+ uData->SSID = sidStr;
+
+ /* Get the SSH folder for the user. */
+ char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp);
+ if (sshFolder == NULL) {
+ goto error;
+ }
+
+ /* Get the authorized key file path */
+ const char *authorizedKeyFile =
+ uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE;
+ char *authorizedKeyPath =
+ g_build_filename(sshFolder, authorizedKeyFile, NULL);
+ uData->sshDirectory = sshFolder;
+ uData->authorizedKeyFile = authorizedKeyPath;
+
+ /* Free */
+ NetApiBufferFree(uBuf);
+ return true;
+error:
+ if (uBuf) {
+ NetApiBufferFree(uBuf);
+ }
+
+ return false;
+}
+
+/*
+ * Gets the list of authorized keys for a user.
+ *
+ * parameters:
+ * username -> Username to retrieve the keys for.
+ * errp -> Error structure that will display any errors through QMP.
+ * returns: List of keys associated with the user.
+ */
+GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username,
+ Error **errp)
+{
+ GuestAuthorizedKeys *keys = NULL;
+ g_auto(GStrv) authKeys = NULL;
+ g_autoptr(GuestAuthorizedKeys) ret = NULL;
+ g_auto(PWindowsUserInfo) userInfo = NULL;
+
+ /* Gets user information */
+ if (!get_user_info(&userInfo, username, errp)) {
+ return NULL;
+ }
+
+ /* Reads authkeys for the user */
+ authKeys = read_authkeys(userInfo->authorizedKeyFile, errp);
+ if (authKeys == NULL) {
+ return NULL;
+ }
+
+ /* Set the GuestAuthorizedKey struct with keys from the file */
+ ret = g_new0(GuestAuthorizedKeys, 1);
+ for (int i = 0; authKeys[i] != NULL; i++) {
+ g_strstrip(authKeys[i]);
+ if (!authKeys[i][0] || authKeys[i][0] == '#') {
+ continue;
+ }
+
+ QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i]));
+ }
+
+ /*
+ * Steal the pointer because it is up for the callee to deallocate the
+ * memory.
+ */
+ keys = g_steal_pointer(&ret);
+ return keys;
+}
+
+/*
+ * Adds an ssh key for a user.
+ *
+ * parameters:
+ * username -> User to add the SSH key to
+ * strList -> Array of keys to add to the list
+ * has_reset -> Whether the keys have been reset
+ * reset -> Boolean to reset the keys (If this is set the existing list will be
+ * cleared) and the other key reset. errp -> Pointer to an error structure that
+ * will get returned over QMP if anything goes wrong.
+ */
+void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
+ bool has_reset, bool reset, Error **errp)
+{
+ g_auto(PWindowsUserInfo) userInfo = NULL;
+ g_auto(GStrv) authkeys = NULL;
+ strList *k;
+ size_t nkeys, nauthkeys;
+
+ /* Make sure the keys given are valid */
+ if (!check_openssh_pub_keys(keys, &nkeys, errp)) {
+ return;
+ }
+
+ /* Gets user information */
+ if (!get_user_info(&userInfo, username, errp)) {
+ return;
+ }
+
+ /* Determine whether we should reset the keys */
+ reset = has_reset && reset;
+ if (!reset) {
+ /* Read existing keys into memory */
+ authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL);
+ }
+
+ /* Check that the SSH key directory exists for the user. */
+ if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) {
+ BOOL success = create_ssh_directory(userInfo, errp);
+ if (!success) {
+ return;
+ }
+ }
+
+ /* Reallocates the buffer to fit the new keys. */
+ nauthkeys = authkeys ? g_strv_length(authkeys) : 0;
+ authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *));
+
+ /* zero out the memory for the reallocated buffer */
+ memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *));
+
+ /* Adds the keys */
+ for (k = keys; k != NULL; k = k->next) {
+ /* Check that the key doesn't already exist */
+ if (g_strv_contains((const gchar *const *)authkeys, k->value)) {
+ continue;
+ }
+
+ authkeys[nauthkeys++] = g_strdup(k->value);
+ }
+
+ /* Write the authkeys to the file. */
+ write_authkeys(userInfo, authkeys, errp);
+}
+
+/*
+ * Removes an SSH key for a user
+ *
+ * parameters:
+ * username -> Username to remove the key from
+ * strList -> List of strings to remove
+ * errp -> Contains any errors that occur.
+ */
+void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys,
+ Error **errp)
+{
+ g_auto(PWindowsUserInfo) userInfo = NULL;
+ g_autofree struct passwd *p = NULL;
+ g_autofree GStrv new_keys = NULL; /* do not own the strings */
+ g_auto(GStrv) authkeys = NULL;
+ GStrv a;
+ size_t nkeys = 0;
+
+ /* Validates the keys passed in by the user */
+ if (!check_openssh_pub_keys(keys, NULL, errp)) {
+ return;
+ }
+
+ /* Gets user information */
+ if (!get_user_info(&userInfo, username, errp)) {
+ return;
+ }
+
+ /* Reads the authkeys for the user */
+ authkeys = read_authkeys(userInfo->authorizedKeyFile, errp);
+ if (authkeys == NULL) {
+ return;
+ }
+
+ /* Create a new buffer to hold the keys */
+ new_keys = g_new0(char *, g_strv_length(authkeys) + 1);
+ for (a = authkeys; *a != NULL; a++) {
+ strList *k;
+
+ /* Filters out keys that are equal to ones the user specified. */
+ for (k = keys; k != NULL; k = k->next) {
+ if (g_str_equal(k->value, *a)) {
+ break;
+ }
+ }
+
+ if (k != NULL) {
+ continue;
+ }
+
+ new_keys[nkeys++] = *a;
+ }
+
+ /* Write the new authkeys to the file. */
+ write_authkeys(userInfo, new_keys, errp);
+}
diff --git a/qga/commands-windows-ssh.h b/qga/commands-windows-ssh.h
new file mode 100644
index 0000000000..40ac67c4d9
--- /dev/null
+++ b/qga/commands-windows-ssh.h
@@ -0,0 +1,26 @@
+/*
+ * Header file for commands-windows-ssh.c
+ *
+ * Copyright Schweitzer Engineering Laboratories. 2024
+ *
+ * Authors:
+ * Aidan Leuck <aidan_leuck@selinc.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib/gstrfuncs.h>
+#include <stdbool.h>
+typedef struct WindowsUserInfo {
+ char *sshDirectory;
+ char *authorizedKeyFile;
+ char *username;
+ char *SSID;
+ bool isAdmin;
+} WindowsUserInfo;
+
+typedef WindowsUserInfo *PWindowsUserInfo;
+
+void free_userInfo(PWindowsUserInfo info);
+G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PWindowsUserInfo, free_userInfo, NULL);
diff --git a/qga/meson.build b/qga/meson.build
index 1c3d2a3d1b..587ec4e5e8 100644
--- a/qga/meson.build
+++ b/qga/meson.build
@@ -66,13 +66,15 @@ qga_ss.add(files(
'guest-agent-command-state.c',
'main.c',
'cutils.c',
+ 'commands-common-ssh.c'
))
if host_os == 'windows'
qga_ss.add(files(
'channel-win32.c',
'commands-win32.c',
'service-win32.c',
- 'vss-win32.c'
+ 'vss-win32.c',
+ 'commands-windows-ssh.c'
))
else
qga_ss.add(files(
@@ -93,7 +95,7 @@ gen_tlb = []
qga_libs = []
if host_os == 'windows'
qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32',
- '-lsetupapi', '-lcfgmgr32']
+ '-lsetupapi', '-lcfgmgr32', '-luserenv']
if have_qga_vss
qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup']
subdir('vss-win32')
@@ -181,13 +183,12 @@ test_env = environment()
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
-# disable qga-ssh-test for now. glib's G_TEST_OPTION_ISOLATE_DIRS triggers
+# disable qga-ssh-test with fuzzing: glib's G_TEST_OPTION_ISOLATE_DIRS triggers
# the leak detector in build-oss-fuzz Gitlab CI test. we should re-enable
# this when an alternative is implemented or when the underlying glib
# issue is identified/fix
-#if host_os != 'windows'
-if false
- srcs = [files('commands-posix-ssh.c')]
+if host_os != 'windows' and not get_option('fuzzing')
+ srcs = [files('commands-common-ssh.c', 'commands-posix-ssh.c')]
i = 0
foreach output: qga_qapi_outputs
if output.startswith('qga-qapi-types') or output.startswith('qga-qapi-visit')
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index d5af155007..b3de1fb6b3 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1026,7 +1026,10 @@
#
# @used-bytes: file system used bytes (since 3.0)
#
-# @total-bytes: non-root file system total bytes (since 3.0)
+# @total-bytes: filesystem capacity in bytes for unprivileged users (since 3.0)
+#
+# @total-bytes-privileged: filesystem capacity in bytes for privileged users
+# (since 9.1)
#
# @disk: an array of disk hardware information that the volume lies
# on, which may be empty if the disk type is not supported
@@ -1036,7 +1039,7 @@
{ 'struct': 'GuestFilesystemInfo',
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
'*used-bytes': 'uint64', '*total-bytes': 'uint64',
- 'disk': ['GuestDiskAddress']} }
+ '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']} }
##
# @guest-get-fsinfo:
@@ -1567,9 +1570,8 @@
{ 'struct': 'GuestAuthorizedKeys',
'data': {
'keys': ['str']
- },
- 'if': 'CONFIG_POSIX' }
-
+ }
+}
##
# @guest-ssh-get-authorized-keys:
@@ -1585,8 +1587,8 @@
##
{ 'command': 'guest-ssh-get-authorized-keys',
'data': { 'username': 'str' },
- 'returns': 'GuestAuthorizedKeys',
- 'if': 'CONFIG_POSIX' }
+ 'returns': 'GuestAuthorizedKeys'
+}
##
# @guest-ssh-add-authorized-keys:
@@ -1604,8 +1606,8 @@
# Since: 5.2
##
{ 'command': 'guest-ssh-add-authorized-keys',
- 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
- 'if': 'CONFIG_POSIX' }
+ 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }
+}
##
# @guest-ssh-remove-authorized-keys:
@@ -1622,8 +1624,8 @@
# Since: 5.2
##
{ 'command': 'guest-ssh-remove-authorized-keys',
- 'data': { 'username': 'str', 'keys': ['str'] },
- 'if': 'CONFIG_POSIX' }
+ 'data': { 'username': 'str', 'keys': ['str'] }
+}
##
# @GuestDiskStats:
diff --git a/qom/object.c b/qom/object.c
index 44ec8f6460..157a45c5f8 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -157,14 +157,6 @@ static bool type_name_is_valid(const char *name)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789-_.");
- /* Allow some legacy names with '+' in it for compatibility reasons */
- if (name[plen] == '+') {
- if (plen >= 17 && g_str_has_prefix(name, "Sun-UltraSparc-I")) {
- /* Allow "Sun-UltraSparc-IV+" and "Sun-UltraSparc-IIIi+" */
- return true;
- }
- }
-
return plen == slen;
}
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 7026895074..ff373a7083 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -435,8 +435,8 @@ if ($chk_branch) {
my @patches;
my %git_commits = ();
my $HASH;
- open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--format=%H %s", $ARGV[0]) ||
- die "$P: git log --reverse --no-merges --format='%H %s' $ARGV[0] failed - $!\n";
+ open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--no-mailmap", "--format=%H %s", $ARGV[0]) ||
+ die "$P: git log --reverse --no-merges --no-mailmap --format='%H %s' $ARGV[0] failed - $!\n";
for my $line (<$HASH>) {
$line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
@@ -460,7 +460,7 @@ if ($chk_branch) {
"-c", "diff.renamelimit=0",
"-c", "diff.renames=True",
"-c", "diff.algorithm=histogram",
- "show",
+ "show", "--no-mailmap",
"--patch-with-stat", $hash) ||
die "$P: git show $hash - $!\n";
while (<$FILE>) {
@@ -1573,7 +1573,7 @@ sub process {
$is_patch = 1;
}
- if ($line =~ /^(Author|From): .* via .*<qemu-devel\@nongnu.org>/) {
+ if ($line =~ /^(Author|From): .* via .*<qemu-\w+\@nongnu\.org>/) {
ERROR("Author email address is mangled by the mailing list\n" . $herecurr);
}
@@ -3078,6 +3078,9 @@ sub process {
if ($line =~ /\b(g_)?assert\(0\)/) {
ERROR("use g_assert_not_reached() instead of assert(0)\n" . $herecurr);
}
+ if ($line =~ /\bstrerrorname_np\(/) {
+ ERROR("use strerror() instead of strerrorname_np()\n" . $herecurr);
+ }
my $non_exit_glib_asserts = qr{g_assert_cmpstr|
g_assert_cmpint|
g_assert_cmpuint|
diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml
index f344d1a850..de0d866a1e 100644
--- a/scripts/ci/setup/build-environment.yml
+++ b/scripts/ci/setup/build-environment.yml
@@ -95,7 +95,6 @@
- libpam0g-dev
- libpcre2-dev
- libpixman-1-dev
- - libpmem-dev
- libpng-dev
- libpulse-dev
- librbd-dev
@@ -107,7 +106,6 @@
- libslirp-dev
- libsnappy-dev
- libspice-protocol-dev
- - libspice-server-dev
- libssh-dev
- libsystemd-dev
- libtasn1-6-dev
@@ -119,7 +117,6 @@
- libvdeplug-dev
- libvirglrenderer-dev
- libvte-2.91-dev
- - libxen-dev
- libxml2-dev
- libzstd-dev
- llvm
@@ -156,6 +153,19 @@
- ansible_facts['distribution'] == 'Ubuntu'
- ansible_facts['distribution_version'] == '22.04'
+ # not all packages are available for all architectures
+ - name: Install additional packages to build QEMU on Ubuntu 22.04
+ package:
+ name:
+ - libpmem-dev
+ - libspice-server-dev
+ - libxen-dev
+ state: present
+ when:
+ - ansible_facts['distribution'] == 'Ubuntu'
+ - ansible_facts['distribution_version'] == '22.04'
+ - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64'
+
- name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 22.04
package:
name:
diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md
index 91be8d1c36..1537e49cd5 100644
--- a/scripts/coverity-scan/COMPONENTS.md
+++ b/scripts/coverity-scan/COMPONENTS.md
@@ -121,7 +121,7 @@ usb
~ (/qemu)?(/hw/usb/.*|/include/hw/usb/.*)
user
- ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/exec/user/.*)
+ ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*)
util
~ (/qemu)?(/util/.*|/include/qemu/.*)
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 0a29d35fdb..6ce5a8b72a 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -143,8 +143,6 @@ meson_options_help() {
printf "%s\n" ' libvduse build VDUSE Library'
printf "%s\n" ' linux-aio Linux AIO support'
printf "%s\n" ' linux-io-uring Linux io_uring support'
- printf "%s\n" ' live-block-migration'
- printf "%s\n" ' block migration in the main migration stream'
printf "%s\n" ' lzfse lzfse support for DMG images'
printf "%s\n" ' lzo lzo compression support'
printf "%s\n" ' malloc-trim enable libc malloc_trim() for memory optimization'
@@ -382,8 +380,6 @@ _meson_option_parse() {
--disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;;
--enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;;
--disable-linux-io-uring) printf "%s" -Dlinux_io_uring=disabled ;;
- --enable-live-block-migration) printf "%s" -Dlive_block_migration=enabled ;;
- --disable-live-block-migration) printf "%s" -Dlive_block_migration=disabled ;;
--localedir=*) quote_sh "-Dlocaledir=$2" ;;
--localstatedir=*) quote_sh "-Dlocalstatedir=$2" ;;
--enable-lzfse) printf "%s" -Dlzfse=enabled ;;
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index d1fdf4182c..79951a841f 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -64,7 +64,7 @@ def gen_call(name: str,
assert arg_type
argstr = '&arg, '
elif arg_type:
- assert not arg_type.variants
+ assert not arg_type.branches
for memb in arg_type.members:
assert not memb.ifcond.is_present()
if memb.need_has():
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index 3cf01e96b6..d1f639981a 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -51,7 +51,7 @@ def gen_param_var(typ: QAPISchemaObjectType) -> str:
Initialize it with the function arguments defined in `gen_event_send`.
"""
- assert not typ.variants
+ assert not typ.branches
ret = mcgen('''
%(c_name)s param = {
''',
diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py
index 5412716617..6a8abe0041 100644
--- a/scripts/qapi/gen.py
+++ b/scripts/qapi/gen.py
@@ -118,7 +118,7 @@ def build_params(arg_type: Optional[QAPISchemaObjectType],
ret += '%s arg' % arg_type.c_param_type()
sep = ', '
elif arg_type:
- assert not arg_type.variants
+ assert not arg_type.branches
for memb in arg_type.members:
assert not memb.ifcond.is_present()
ret += sep
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 4679b1bc2c..86c075a6ad 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -26,6 +26,8 @@ from .common import c_name, mcgen
from .gen import QAPISchemaMonolithicCVisitor
from .schema import (
QAPISchema,
+ QAPISchemaAlternatives,
+ QAPISchemaBranches,
QAPISchemaArrayType,
QAPISchemaBuiltinType,
QAPISchemaEntity,
@@ -36,7 +38,6 @@ from .schema import (
QAPISchemaObjectTypeMember,
QAPISchemaType,
QAPISchemaVariant,
- QAPISchemaVariants,
)
from .source import QAPISourceInfo
@@ -335,24 +336,24 @@ const QLitObject %(c_name)s = %(c_string)s;
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants]) -> None:
+ branches: Optional[QAPISchemaBranches]) -> None:
obj: SchemaInfoObject = {
'members': [self._gen_object_member(m) for m in members]
}
- if variants:
- obj['tag'] = variants.tag_member.name
- obj['variants'] = [self._gen_variant(v) for v in variants.variants]
+ if branches:
+ obj['tag'] = branches.tag_member.name
+ obj['variants'] = [self._gen_variant(v) for v in branches.variants]
self._gen_tree(name, 'object', obj, ifcond, features)
def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
- variants: QAPISchemaVariants) -> None:
+ alternatives: QAPISchemaAlternatives) -> None:
self._gen_tree(
name, 'alternate',
{'members': [Annotated({'type': self._use_type(m.type)},
m.ifcond)
- for m in variants.variants]},
+ for m in alternatives.variants]},
ifcond, features
)
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 5924947fc3..721c470d2b 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -215,7 +215,7 @@ class QAPISchemaVisitor:
features: List[QAPISchemaFeature],
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants],
+ branches: Optional[QAPISchemaBranches],
) -> None:
pass
@@ -226,7 +226,7 @@ class QAPISchemaVisitor:
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants],
+ branches: Optional[QAPISchemaBranches],
) -> None:
pass
@@ -236,7 +236,7 @@ class QAPISchemaVisitor:
info: Optional[QAPISourceInfo],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
- variants: QAPISchemaVariants,
+ alternatives: QAPISchemaAlternatives,
) -> None:
pass
@@ -524,20 +524,20 @@ class QAPISchemaObjectType(QAPISchemaType):
features: Optional[List[QAPISchemaFeature]],
base: Optional[str],
local_members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants],
+ branches: Optional[QAPISchemaBranches],
):
- # struct has local_members, optional base, and no variants
- # union has base, variants, and no local_members
+ # struct has local_members, optional base, and no branches
+ # union has base, branches, and no local_members
super().__init__(name, info, doc, ifcond, features)
- self.meta = 'union' if variants else 'struct'
+ self.meta = 'union' if branches else 'struct'
for m in local_members:
m.set_defined_in(name)
- if variants is not None:
- variants.set_defined_in(name)
+ if branches is not None:
+ branches.set_defined_in(name)
self._base_name = base
self.base = None
self.local_members = local_members
- self.variants = variants
+ self.branches = branches
self.members: List[QAPISchemaObjectTypeMember]
self._check_complete = False
@@ -561,7 +561,7 @@ class QAPISchemaObjectType(QAPISchemaType):
self.base = schema.resolve_type(self._base_name, self.info,
"'base'")
if (not isinstance(self.base, QAPISchemaObjectType)
- or self.base.variants):
+ or self.base.branches):
raise QAPISemError(
self.info,
"'base' requires a struct type, %s isn't"
@@ -577,9 +577,9 @@ class QAPISchemaObjectType(QAPISchemaType):
# Cast down to the subtype.
members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
- if self.variants:
- self.variants.check(schema, seen)
- self.variants.check_clash(self.info, seen)
+ if self.branches:
+ self.branches.check(schema, seen)
+ self.branches.check_clash(self.info, seen)
self.members = members
self._check_complete = True # mark completed
@@ -595,8 +595,8 @@ class QAPISchemaObjectType(QAPISchemaType):
assert self._checked
for m in self.members:
m.check_clash(info, seen)
- if self.variants:
- self.variants.check_clash(info, seen)
+ if self.branches:
+ self.branches.check_clash(info, seen)
def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
super().connect_doc(doc)
@@ -612,7 +612,7 @@ class QAPISchemaObjectType(QAPISchemaType):
return self.name.startswith('q_')
def is_empty(self) -> bool:
- return not self.members and not self.variants
+ return not self.members and not self.branches
def has_conditional_members(self) -> bool:
return any(m.ifcond.is_present() for m in self.members)
@@ -635,10 +635,10 @@ class QAPISchemaObjectType(QAPISchemaType):
super().visit(visitor)
visitor.visit_object_type(
self.name, self.info, self.ifcond, self.features,
- self.base, self.local_members, self.variants)
+ self.base, self.local_members, self.branches)
visitor.visit_object_type_flat(
self.name, self.info, self.ifcond, self.features,
- self.members, self.variants)
+ self.members, self.branches)
class QAPISchemaAlternateType(QAPISchemaType):
@@ -651,25 +651,25 @@ class QAPISchemaAlternateType(QAPISchemaType):
doc: Optional[QAPIDoc],
ifcond: Optional[QAPISchemaIfCond],
features: List[QAPISchemaFeature],
- variants: QAPISchemaVariants,
+ alternatives: QAPISchemaAlternatives,
):
super().__init__(name, info, doc, ifcond, features)
- assert variants.tag_member
- variants.set_defined_in(name)
- variants.tag_member.set_defined_in(self.name)
- self.variants = variants
+ assert alternatives.tag_member
+ alternatives.set_defined_in(name)
+ alternatives.tag_member.set_defined_in(self.name)
+ self.alternatives = alternatives
def check(self, schema: QAPISchema) -> None:
super().check(schema)
- self.variants.tag_member.check(schema)
- # Not calling self.variants.check_clash(), because there's nothing
- # to clash with
- self.variants.check(schema, {})
+ self.alternatives.tag_member.check(schema)
+ # Not calling self.alternatives.check_clash(), because there's
+ # nothing to clash with
+ self.alternatives.check(schema, {})
# Alternate branch names have no relation to the tag enum values;
# so we have to check for potential name collisions ourselves.
seen: Dict[str, QAPISchemaMember] = {}
types_seen: Dict[str, str] = {}
- for v in self.variants.variants:
+ for v in self.alternatives.variants:
v.check_clash(self.info, seen)
qtype = v.type.alternate_qtype()
if not qtype:
@@ -700,7 +700,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
super().connect_doc(doc)
doc = doc or self.doc
- for v in self.variants.variants:
+ for v in self.alternatives.variants:
v.connect_doc(doc)
def c_type(self) -> str:
@@ -712,94 +712,86 @@ class QAPISchemaAlternateType(QAPISchemaType):
def visit(self, visitor: QAPISchemaVisitor) -> None:
super().visit(visitor)
visitor.visit_alternate_type(
- self.name, self.info, self.ifcond, self.features, self.variants)
+ self.name, self.info, self.ifcond, self.features,
+ self.alternatives)
class QAPISchemaVariants:
def __init__(
self,
- tag_name: Optional[str],
info: QAPISourceInfo,
- tag_member: Optional[QAPISchemaObjectTypeMember],
variants: List[QAPISchemaVariant],
):
- # Unions pass tag_name but not tag_member.
- # Alternates pass tag_member but not tag_name.
- # After check(), tag_member is always set.
- assert bool(tag_member) != bool(tag_name)
- assert (isinstance(tag_name, str) or
- isinstance(tag_member, QAPISchemaObjectTypeMember))
- self._tag_name = tag_name
self.info = info
- self._tag_member = tag_member
+ self.tag_member: QAPISchemaObjectTypeMember
self.variants = variants
- @property
- def tag_member(self) -> QAPISchemaObjectTypeMember:
- if self._tag_member is None:
- raise RuntimeError(
- "QAPISchemaVariants has no tag_member property until "
- "after check() has been run."
- )
- return self._tag_member
-
def set_defined_in(self, name: str) -> None:
for v in self.variants:
v.set_defined_in(name)
def check(
- self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
+ self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
) -> None:
- if self._tag_name: # union
- # We need to narrow the member type:
- tmp = seen.get(c_name(self._tag_name))
- assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember)
- self._tag_member = tmp
-
- base = "'base'"
- # Pointing to the base type when not implicit would be
- # nice, but we don't know it here
- if not self._tag_member or self._tag_name != self._tag_member.name:
- raise QAPISemError(
- self.info,
- "discriminator '%s' is not a member of %s"
- % (self._tag_name, base))
- # Here we do:
- assert self.tag_member.defined_in
- base_type = schema.lookup_type(self.tag_member.defined_in)
- assert base_type
- if not base_type.is_implicit():
- base = "base type '%s'" % self.tag_member.defined_in
- if not isinstance(self.tag_member.type, QAPISchemaEnumType):
- raise QAPISemError(
- self.info,
- "discriminator member '%s' of %s must be of enum type"
- % (self._tag_name, base))
- if self.tag_member.optional:
- raise QAPISemError(
- self.info,
- "discriminator member '%s' of %s must not be optional"
- % (self._tag_name, base))
- if self.tag_member.ifcond.is_present():
- raise QAPISemError(
- self.info,
- "discriminator member '%s' of %s must not be conditional"
- % (self._tag_name, base))
- else: # alternate
- assert self._tag_member
- assert isinstance(self.tag_member.type, QAPISchemaEnumType)
- assert not self.tag_member.optional
- assert not self.tag_member.ifcond.is_present()
- if self._tag_name: # union
- # branches that are not explicitly covered get an empty type
- assert self.tag_member.defined_in
- cases = {v.name for v in self.variants}
- for m in self.tag_member.type.members:
- if m.name not in cases:
- v = QAPISchemaVariant(m.name, self.info,
- 'q_empty', m.ifcond)
- v.set_defined_in(self.tag_member.defined_in)
- self.variants.append(v)
+ for v in self.variants:
+ v.check(schema)
+
+
+class QAPISchemaBranches(QAPISchemaVariants):
+ def __init__(self,
+ info: QAPISourceInfo,
+ variants: List[QAPISchemaVariant],
+ tag_name: str):
+ super().__init__(info, variants)
+ self._tag_name = tag_name
+
+ def check(
+ self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
+ ) -> None:
+ # We need to narrow the member type:
+ tag_member = seen.get(c_name(self._tag_name))
+ assert (tag_member is None
+ or isinstance(tag_member, QAPISchemaObjectTypeMember))
+
+ base = "'base'"
+ # Pointing to the base type when not implicit would be
+ # nice, but we don't know it here
+ if not tag_member or self._tag_name != tag_member.name:
+ raise QAPISemError(
+ self.info,
+ "discriminator '%s' is not a member of %s"
+ % (self._tag_name, base))
+ self.tag_member = tag_member
+ # Here we do:
+ assert tag_member.defined_in
+ base_type = schema.lookup_type(tag_member.defined_in)
+ assert base_type
+ if not base_type.is_implicit():
+ base = "base type '%s'" % tag_member.defined_in
+ if not isinstance(tag_member.type, QAPISchemaEnumType):
+ raise QAPISemError(
+ self.info,
+ "discriminator member '%s' of %s must be of enum type"
+ % (self._tag_name, base))
+ if tag_member.optional:
+ raise QAPISemError(
+ self.info,
+ "discriminator member '%s' of %s must not be optional"
+ % (self._tag_name, base))
+ if tag_member.ifcond.is_present():
+ raise QAPISemError(
+ self.info,
+ "discriminator member '%s' of %s must not be conditional"
+ % (self._tag_name, base))
+ # branches that are not explicitly covered get an empty type
+ assert tag_member.defined_in
+ cases = {v.name for v in self.variants}
+ for m in tag_member.type.members:
+ if m.name not in cases:
+ v = QAPISchemaVariant(m.name, self.info,
+ 'q_empty', m.ifcond)
+ v.set_defined_in(tag_member.defined_in)
+ self.variants.append(v)
if not self.variants:
raise QAPISemError(self.info, "union has no branches")
for v in self.variants:
@@ -807,11 +799,11 @@ class QAPISchemaVariants:
# Union names must match enum values; alternate names are
# checked separately. Use 'seen' to tell the two apart.
if seen:
- if v.name not in self.tag_member.type.member_names():
+ if v.name not in tag_member.type.member_names():
raise QAPISemError(
self.info,
"branch '%s' is not a value of %s"
- % (v.name, self.tag_member.type.describe()))
+ % (v.name, tag_member.type.describe()))
if not isinstance(v.type, QAPISchemaObjectType):
raise QAPISemError(
self.info,
@@ -833,6 +825,23 @@ class QAPISchemaVariants:
v.type.check_clash(info, dict(seen))
+class QAPISchemaAlternatives(QAPISchemaVariants):
+ def __init__(self,
+ info: QAPISourceInfo,
+ variants: List[QAPISchemaVariant],
+ tag_member: QAPISchemaObjectTypeMember):
+ super().__init__(info, variants)
+ self.tag_member = tag_member
+
+ def check(
+ self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
+ ) -> None:
+ super().check(schema, seen)
+ assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+ assert not self.tag_member.optional
+ assert not self.tag_member.ifcond.is_present()
+
+
class QAPISchemaMember:
""" Represents object members, enum members and features """
role = 'member'
@@ -1019,7 +1028,7 @@ class QAPISchemaCommand(QAPISchemaDefinition):
"command's 'data' cannot take %s"
% arg_type.describe())
self.arg_type = arg_type
- if self.arg_type.variants and not self.boxed:
+ if self.arg_type.branches and not self.boxed:
raise QAPISemError(
self.info,
"command's 'data' can take %s only with 'boxed': true"
@@ -1087,7 +1096,7 @@ class QAPISchemaEvent(QAPISchemaDefinition):
"event's 'data' cannot take %s"
% typ.describe())
self.arg_type = typ
- if self.arg_type.variants and not self.boxed:
+ if self.arg_type.branches and not self.boxed:
raise QAPISemError(
self.info,
"event's 'data' can take %s only with 'boxed': true"
@@ -1388,8 +1397,8 @@ class QAPISchema:
self._def_definition(
QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
base, members,
- QAPISchemaVariants(
- tag_name, info, None, variants)))
+ QAPISchemaBranches(
+ info, variants, tag_name)))
def _def_alternate_type(self, expr: QAPIExpression) -> None:
name = expr['alternate']
@@ -1407,7 +1416,7 @@ class QAPISchema:
self._def_definition(
QAPISchemaAlternateType(
name, info, expr.doc, ifcond, features,
- QAPISchemaVariants(None, info, tag_member, variants)))
+ QAPISchemaAlternatives(info, variants, tag_member)))
def _def_command(self, expr: QAPIExpression) -> None:
name = expr['command']
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index c39d054d2c..0dd0b00ada 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -23,6 +23,8 @@ from .gen import (
)
from .schema import (
QAPISchema,
+ QAPISchemaAlternatives,
+ QAPISchemaBranches,
QAPISchemaEnumMember,
QAPISchemaFeature,
QAPISchemaIfCond,
@@ -169,7 +171,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond,
if not isinstance(obj, QAPISchemaObjectType):
continue
ret += gen_object(obj.name, obj.ifcond, obj.base,
- obj.local_members, obj.variants)
+ obj.local_members, obj.branches)
ret += mcgen('''
@@ -348,13 +350,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
features: List[QAPISchemaFeature],
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants]) -> None:
+ branches: Optional[QAPISchemaBranches]) -> None:
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
with ifcontext(ifcond, self._genh):
self._genh.preamble_add(gen_fwd_object_or_array(name))
- self._genh.add(gen_object(name, ifcond, base, members, variants))
+ self._genh.add(gen_object(name, ifcond, base, members, branches))
with ifcontext(ifcond, self._genh, self._genc):
if base and not base.is_implicit():
self._genh.add(gen_upcast(name, base))
@@ -369,11 +371,11 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
info: Optional[QAPISourceInfo],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
- variants: QAPISchemaVariants) -> None:
+ alternatives: QAPISchemaAlternatives) -> None:
with ifcontext(ifcond, self._genh):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, ifcond, None,
- [variants.tag_member], variants))
+ [alternatives.tag_member], alternatives))
with ifcontext(ifcond, self._genh, self._genc):
self._gen_type_cleanup(name)
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index a21b7b1468..e766acaac9 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -28,6 +28,8 @@ from .gen import (
)
from .schema import (
QAPISchema,
+ QAPISchemaAlternatives,
+ QAPISchemaBranches,
QAPISchemaEnumMember,
QAPISchemaEnumType,
QAPISchemaFeature,
@@ -35,7 +37,6 @@ from .schema import (
QAPISchemaObjectType,
QAPISchemaObjectTypeMember,
QAPISchemaType,
- QAPISchemaVariants,
)
from .source import QAPISourceInfo
@@ -63,7 +64,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
def gen_visit_object_members(name: str,
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants]) -> str:
+ branches: Optional[QAPISchemaBranches]) -> str:
ret = mcgen('''
bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
@@ -131,8 +132,8 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''')
ret += memb.ifcond.gen_endif()
- if variants:
- tag_member = variants.tag_member
+ if branches:
+ tag_member = branches.tag_member
assert isinstance(tag_member.type, QAPISchemaEnumType)
ret += mcgen('''
@@ -140,7 +141,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''',
c_name=c_name(tag_member.name))
- for var in variants.variants:
+ for var in branches.variants:
case_str = c_enum_const(tag_member.type.name, var.name,
tag_member.type.prefix)
ret += var.ifcond.gen_if()
@@ -222,7 +223,8 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
c_name=c_name(name))
-def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
+def gen_visit_alternate(name: str,
+ alternatives: QAPISchemaAlternatives) -> str:
ret = mcgen('''
bool visit_type_%(c_name)s(Visitor *v, const char *name,
@@ -244,7 +246,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
''',
c_name=c_name(name))
- for var in variants.variants:
+ for var in alternatives.variants:
ret += var.ifcond.gen_if()
ret += mcgen('''
case %(case)s:
@@ -393,14 +395,14 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
features: List[QAPISchemaFeature],
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
- variants: Optional[QAPISchemaVariants]) -> None:
+ branches: Optional[QAPISchemaBranches]) -> None:
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_members_decl(name))
self._genc.add(gen_visit_object_members(name, base,
- members, variants))
+ members, branches))
# TODO Worth changing the visitor signature, so we could
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
@@ -413,10 +415,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
info: Optional[QAPISourceInfo],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
- variants: QAPISchemaVariants) -> None:
+ alternatives: QAPISchemaAlternatives) -> None:
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
- self._genc.add(gen_visit_alternate(name, variants))
+ self._genc.add(gen_visit_alternate(name, alternatives))
def gen_visit(schema: QAPISchema,
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index 36f3e91fe4..8963c39189 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -27,6 +27,8 @@
# types like "__u64". This work is done in the cp_portable function.
tmpdir=$(mktemp -d)
+hdrdir="$tmpdir/headers"
+blddir="$tmpdir/build"
linux="$1"
output="$2"
@@ -110,56 +112,56 @@ for arch in $ARCHLIST; do
arch_var=ARCH
fi
- make -C "$linux" INSTALL_HDR_PATH="$tmpdir" $arch_var=$arch headers_install
+ make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install
rm -rf "$output/linux-headers/asm-$arch"
mkdir -p "$output/linux-headers/asm-$arch"
for header in kvm.h unistd.h bitsperlong.h mman.h; do
- cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch"
+ cp "$hdrdir/include/asm/$header" "$output/linux-headers/asm-$arch"
done
if [ $arch = mips ]; then
- cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/"
- cp "$tmpdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/"
- cp "$tmpdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/"
- cp "$tmpdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/"
+ cp "$hdrdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/"
+ cp "$hdrdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/"
+ cp "$hdrdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/"
+ cp "$hdrdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/"
fi
if [ $arch = powerpc ]; then
- cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/"
- cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/"
+ cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/"
+ cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/"
fi
rm -rf "$output/include/standard-headers/asm-$arch"
mkdir -p "$output/include/standard-headers/asm-$arch"
if [ $arch = s390 ]; then
- cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/"
- cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/"
- cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/"
+ cp_portable "$hdrdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/"
+ cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/"
+ cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/"
fi
if [ $arch = arm ]; then
- cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/"
- cp "$tmpdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/"
- cp "$tmpdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/"
+ cp "$hdrdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/"
+ cp "$hdrdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/"
+ cp "$hdrdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/"
fi
if [ $arch = arm64 ]; then
- cp "$tmpdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/"
+ cp "$hdrdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/"
fi
if [ $arch = x86 ]; then
- cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/"
- cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/"
- cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/"
- cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch"
+ cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/"
+ cp "$hdrdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/"
+ cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/"
+ cp_portable "$hdrdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch"
# Remove everything except the macros from bootparam.h avoiding the
# unnecessary import of several video/ist/etc headers
sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \
- "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h"
- cp_portable "$tmpdir/bootparam.h" \
+ "$hdrdir/include/asm/bootparam.h" > "$hdrdir/bootparam.h"
+ cp_portable "$hdrdir/bootparam.h" \
"$output/include/standard-headers/asm-$arch"
- cp_portable "$tmpdir/include/asm/setup_data.h" \
+ cp_portable "$hdrdir/include/asm/setup_data.h" \
"$output/standard-headers/asm-x86"
fi
if [ $arch = riscv ]; then
- cp "$tmpdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
+ cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
fi
done
arch=
@@ -169,13 +171,13 @@ mkdir -p "$output/linux-headers/linux"
for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \
psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \
vduse.h iommufd.h bits.h; do
- cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
+ cp "$hdrdir/include/linux/$header" "$output/linux-headers/linux"
done
rm -rf "$output/linux-headers/asm-generic"
mkdir -p "$output/linux-headers/asm-generic"
for header in unistd.h bitsperlong.h mman-common.h mman.h hugetlb_encode.h; do
- cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic"
+ cp "$hdrdir/include/asm-generic/$header" "$output/linux-headers/asm-generic"
done
if [ -L "$linux/source" ]; then
@@ -210,23 +212,23 @@ EOF
rm -rf "$output/include/standard-headers/linux"
mkdir -p "$output/include/standard-headers/linux"
-for i in "$tmpdir"/include/linux/*virtio*.h \
- "$tmpdir/include/linux/qemu_fw_cfg.h" \
- "$tmpdir/include/linux/fuse.h" \
- "$tmpdir/include/linux/input.h" \
- "$tmpdir/include/linux/input-event-codes.h" \
- "$tmpdir/include/linux/udmabuf.h" \
- "$tmpdir/include/linux/pci_regs.h" \
- "$tmpdir/include/linux/ethtool.h" \
- "$tmpdir/include/linux/const.h" \
- "$tmpdir/include/linux/kernel.h" \
- "$tmpdir/include/linux/vhost_types.h" \
- "$tmpdir/include/linux/sysinfo.h" \
- "$tmpdir/include/misc/pvpanic.h"; do
+for i in "$hdrdir"/include/linux/*virtio*.h \
+ "$hdrdir/include/linux/qemu_fw_cfg.h" \
+ "$hdrdir/include/linux/fuse.h" \
+ "$hdrdir/include/linux/input.h" \
+ "$hdrdir/include/linux/input-event-codes.h" \
+ "$hdrdir/include/linux/udmabuf.h" \
+ "$hdrdir/include/linux/pci_regs.h" \
+ "$hdrdir/include/linux/ethtool.h" \
+ "$hdrdir/include/linux/const.h" \
+ "$hdrdir/include/linux/kernel.h" \
+ "$hdrdir/include/linux/vhost_types.h" \
+ "$hdrdir/include/linux/sysinfo.h" \
+ "$hdrdir/include/misc/pvpanic.h"; do
cp_portable "$i" "$output/include/standard-headers/linux"
done
mkdir -p "$output/include/standard-headers/drm"
-cp_portable "$tmpdir/include/drm/drm_fourcc.h" \
+cp_portable "$hdrdir/include/drm/drm_fourcc.h" \
"$output/include/standard-headers/drm"
cat <<EOF >$output/include/standard-headers/linux/types.h
diff --git a/stubs/meson.build b/stubs/meson.build
index 8ee1fd5753..3b9d42023c 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -21,12 +21,12 @@ if have_block
stub_ss.add(files('migr-blocker.c'))
stub_ss.add(files('physmem.c'))
stub_ss.add(files('ram-block.c'))
- stub_ss.add(files('replay-tools.c'))
stub_ss.add(files('runstate-check.c'))
stub_ss.add(files('uuid.c'))
endif
if have_block or have_ga
+ stub_ss.add(files('replay-tools.c'))
# stubs for hooks in util/main-loop.c, util/async.c etc.
stub_ss.add(files('cpus-get-virtual-clock.c'))
stub_ss.add(files('icount.c'))
@@ -45,6 +45,10 @@ if have_block or have_ga
stub_ss.add(files('qmp-quit.c'))
endif
+if have_ga
+ stub_ss.add(files('error-printf.c'))
+endif
+
if have_block or have_user
stub_ss.add(files('qtest.c'))
stub_ss.add(files('vm-stop.c'))
diff --git a/stubs/target-monitor-defs.c b/stubs/target-monitor-defs.c
index ac07b19064..35a0a34277 100644
--- a/stubs/target-monitor-defs.c
+++ b/stubs/target-monitor-defs.c
@@ -1,6 +1,5 @@
#include "qemu/osdep.h"
-
-const MonitorDef *target_monitor_defs(void);
+#include "monitor/hmp-target.h"
const MonitorDef *target_monitor_defs(void)
{
diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c
new file mode 100644
index 0000000000..bddda6fa37
--- /dev/null
+++ b/system/device_tree-stub.c
@@ -0,0 +1,10 @@
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
+
+#ifdef CONFIG_FDT
+void qmp_dumpdtb(const char *filename, Error **errp)
+{
+ error_setg(errp, "This machine doesn't have a FDT");
+}
+#endif
diff --git a/system/device_tree.c b/system/device_tree.c
index eb5166ca36..2e38259d34 100644
--- a/system/device_tree.c
+++ b/system/device_tree.c
@@ -668,20 +668,6 @@ void qmp_dumpdtb(const char *filename, Error **errp)
}
}
-void hmp_dumpdtb(Monitor *mon, const QDict *qdict)
-{
- const char *filename = qdict_get_str(qdict, "filename");
- Error *local_err = NULL;
-
- qmp_dumpdtb(filename, &local_err);
-
- if (hmp_handle_error(mon, local_err)) {
- return;
- }
-
- info_report("dtb dumped to %s", filename);
-}
-
void qemu_fdt_randomize_seeds(void *fdt)
{
int noffset, poffset, len;
diff --git a/system/dma-helpers.c b/system/dma-helpers.c
index 9b221cf94e..74013308f5 100644
--- a/system/dma-helpers.c
+++ b/system/dma-helpers.c
@@ -169,7 +169,7 @@ static void dma_blk_cb(void *opaque, int ret)
if (dbs->iov.size == 0) {
trace_dma_map_wait(dbs);
dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs);
- cpu_register_map_client(dbs->bh);
+ address_space_register_map_client(dbs->sg->as, dbs->bh);
return;
}
@@ -197,7 +197,7 @@ static void dma_aio_cancel(BlockAIOCB *acb)
}
if (dbs->bh) {
- cpu_unregister_map_client(dbs->bh);
+ address_space_unregister_map_client(dbs->sg->as, dbs->bh);
qemu_bh_delete(dbs->bh);
dbs->bh = NULL;
}
diff --git a/system/memory.c b/system/memory.c
index 49f1cb2c38..9540caa8a1 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -2179,7 +2179,7 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
/* Called with rcu_read_lock held. */
bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only,
- bool *mr_has_discard_manager)
+ bool *mr_has_discard_manager, Error **errp)
{
MemoryRegion *mr;
hwaddr xlat;
@@ -2197,7 +2197,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
mr = address_space_translate(&address_space_memory, iotlb->translated_addr,
&xlat, &len, writable, MEMTXATTRS_UNSPECIFIED);
if (!memory_region_is_ram(mr)) {
- error_report("iommu map to non memory area %" HWADDR_PRIx "", xlat);
+ error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "", xlat);
return false;
} else if (memory_region_has_ram_discard_manager(mr)) {
RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
@@ -2216,8 +2216,8 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
* were already restored before IOMMUs are restored.
*/
if (!ram_discard_manager_is_populated(rdm, &tmp)) {
- error_report("iommu map to discarded memory (e.g., unplugged via"
- " virtio-mem): %" HWADDR_PRIx "",
+ error_setg(errp, "iommu map to discarded memory (e.g., unplugged"
+ " via virtio-mem): %" HWADDR_PRIx "",
iotlb->translated_addr);
return false;
}
@@ -2228,7 +2228,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
* check that it did not truncate too much.
*/
if (len & iotlb->addr_mask) {
- error_report("iommu has granularity incompatible with target AS");
+ error_setg(errp, "iommu has granularity incompatible with target AS");
return false;
}
@@ -3174,6 +3174,9 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
as->ioeventfds = NULL;
QTAILQ_INIT(&as->listeners);
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
+ as->bounce.in_use = false;
+ qemu_mutex_init(&as->map_client_list_lock);
+ QLIST_INIT(&as->map_client_list);
as->name = g_strdup(name ? name : "anonymous");
address_space_update_topology(as);
address_space_update_ioeventfds(as);
@@ -3181,6 +3184,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
static void do_address_space_destroy(AddressSpace *as)
{
+ assert(!qatomic_read(&as->bounce.in_use));
+ assert(QLIST_EMPTY(&as->map_client_list));
+ qemu_mutex_destroy(&as->map_client_list_lock);
+
assert(QTAILQ_EMPTY(&as->listeners));
flatview_unref(as->current_map);
diff --git a/system/meson.build b/system/meson.build
index 25e2117250..a296270cb0 100644
--- a/system/meson.build
+++ b/system/meson.build
@@ -32,7 +32,9 @@ if have_tpm
endif
system_ss.add(when: seccomp, if_true: files('qemu-seccomp.c'))
-system_ss.add(when: fdt, if_true: files('device_tree.c'))
+system_ss.add(when: 'CONFIG_DEVICE_TREE',
+ if_true: [fdt, files('device_tree.c')],
+ if_false: files('device_tree-stub.c'))
if host_os == 'linux'
system_ss.add(files('async-teardown.c'))
endif
diff --git a/system/physmem.c b/system/physmem.c
index 1a81c226ba..342b7a8fd4 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -25,12 +25,14 @@
#include "qemu/cacheflush.h"
#include "qemu/hbitmap.h"
#include "qemu/madvise.h"
+#include "qemu/lockable.h"
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
#endif /* CONFIG_TCG */
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/target_page.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
@@ -158,12 +160,12 @@ static void tcg_commit(MemoryListener *listener);
* @memory_dispatch: its dispatch pointer (cached, RCU protected)
* @tcg_as_listener: listener for tracking changes to the AddressSpace
*/
-struct CPUAddressSpace {
+typedef struct CPUAddressSpace {
CPUState *cpu;
AddressSpace *as;
struct AddressSpaceDispatch *memory_dispatch;
MemoryListener tcg_as_listener;
-};
+} CPUAddressSpace;
struct DirtyBitmapSnapshot {
ram_addr_t start;
@@ -2188,43 +2190,28 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
}
#endif /* !_WIN32 */
-/* Return a host pointer to ram allocated with qemu_ram_alloc.
- * This should not be used for general purpose DMA. Use address_space_map
- * or address_space_rw instead. For local memory (e.g. video ram) that the
- * device owns, use memory_region_get_ram_ptr.
+/*
+ * Return a host pointer to guest's ram.
+ * For Xen, foreign mappings get created if they don't already exist.
*
- * Called within RCU critical section.
- */
-void *qemu_map_ram_ptr(RAMBlock *block, ram_addr_t addr)
-{
- if (block == NULL) {
- block = qemu_get_ram_block(addr);
- addr -= block->offset;
- }
-
- if (xen_enabled() && block->host == NULL) {
- /* We need to check if the requested address is in the RAM
- * because we don't want to map the entire memory in QEMU.
- * In that case just map until the end of the page.
- */
- if (block->offset == 0) {
- return xen_map_cache(addr, 0, 0, false);
- }
-
- block->host = xen_map_cache(block->offset, block->max_length, 1, false);
- }
- return ramblock_ptr(block, addr);
-}
-
-/* Return a host pointer to guest's ram. Similar to qemu_map_ram_ptr
- * but takes a size argument.
+ * @block: block for the RAM to lookup (optional and may be NULL).
+ * @addr: address within the memory region.
+ * @size: pointer to requested size (optional and may be NULL).
+ * size may get modified and return a value smaller than
+ * what was requested.
+ * @lock: wether to lock the mapping in xen-mapcache until invalidated.
+ * @is_write: hint wether to map RW or RO in the xen-mapcache.
+ * (optional and may always be set to true).
*
* Called within RCU critical section.
*/
static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr,
- hwaddr *size, bool lock)
+ hwaddr *size, bool lock,
+ bool is_write)
{
- if (*size == 0) {
+ hwaddr len = 0;
+
+ if (size && *size == 0) {
return NULL;
}
@@ -2232,7 +2219,10 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr,
block = qemu_get_ram_block(addr);
addr -= block->offset;
}
- *size = MIN(*size, block->max_length - addr);
+ if (size) {
+ *size = MIN(*size, block->max_length - addr);
+ len = *size;
+ }
if (xen_enabled() && block->host == NULL) {
/* We need to check if the requested address is in the RAM
@@ -2240,15 +2230,31 @@ static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr,
* In that case just map the requested area.
*/
if (block->offset == 0) {
- return xen_map_cache(addr, *size, lock, lock);
+ return xen_map_cache(block->mr, addr, len, lock, lock,
+ is_write);
}
- block->host = xen_map_cache(block->offset, block->max_length, 1, lock);
+ block->host = xen_map_cache(block->mr, block->offset,
+ block->max_length, 1,
+ lock, is_write);
}
return ramblock_ptr(block, addr);
}
+/*
+ * Return a host pointer to ram allocated with qemu_ram_alloc.
+ * This should not be used for general purpose DMA. Use address_space_map
+ * or address_space_rw instead. For local memory (e.g. video ram) that the
+ * device owns, use memory_region_get_ram_ptr.
+ *
+ * Called within RCU critical section.
+ */
+void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
+{
+ return qemu_ram_ptr_length(ram_block, addr, NULL, false, true);
+}
+
/* Return the offset of a hostpointer within a ramblock */
ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host)
{
@@ -2756,7 +2762,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs,
} else {
/* RAM case */
uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
- false);
+ false, true);
memmove(ram_ptr, buf, *l);
invalidate_and_set_dirty(mr, mr_addr, *l);
@@ -2849,7 +2855,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf,
} else {
/* RAM case */
uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
- false);
+ false, false);
memcpy(buf, ram_ptr, *l);
@@ -3041,55 +3047,36 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len)
NULL, len, FLUSH_CACHE);
}
-typedef struct {
- MemoryRegion *mr;
- void *buffer;
- hwaddr addr;
- hwaddr len;
- bool in_use;
-} BounceBuffer;
-
-static BounceBuffer bounce;
-
-typedef struct MapClient {
- QEMUBH *bh;
- QLIST_ENTRY(MapClient) link;
-} MapClient;
-
-QemuMutex map_client_list_lock;
-static QLIST_HEAD(, MapClient) map_client_list
- = QLIST_HEAD_INITIALIZER(map_client_list);
-
-static void cpu_unregister_map_client_do(MapClient *client)
+static void
+address_space_unregister_map_client_do(AddressSpaceMapClient *client)
{
QLIST_REMOVE(client, link);
g_free(client);
}
-static void cpu_notify_map_clients_locked(void)
+static void address_space_notify_map_clients_locked(AddressSpace *as)
{
- MapClient *client;
+ AddressSpaceMapClient *client;
- while (!QLIST_EMPTY(&map_client_list)) {
- client = QLIST_FIRST(&map_client_list);
+ while (!QLIST_EMPTY(&as->map_client_list)) {
+ client = QLIST_FIRST(&as->map_client_list);
qemu_bh_schedule(client->bh);
- cpu_unregister_map_client_do(client);
+ address_space_unregister_map_client_do(client);
}
}
-void cpu_register_map_client(QEMUBH *bh)
+void address_space_register_map_client(AddressSpace *as, QEMUBH *bh)
{
- MapClient *client = g_malloc(sizeof(*client));
+ AddressSpaceMapClient *client = g_malloc(sizeof(*client));
- qemu_mutex_lock(&map_client_list_lock);
+ QEMU_LOCK_GUARD(&as->map_client_list_lock);
client->bh = bh;
- QLIST_INSERT_HEAD(&map_client_list, client, link);
+ QLIST_INSERT_HEAD(&as->map_client_list, client, link);
/* Write map_client_list before reading in_use. */
smp_mb();
- if (!qatomic_read(&bounce.in_use)) {
- cpu_notify_map_clients_locked();
+ if (!qatomic_read(&as->bounce.in_use)) {
+ address_space_notify_map_clients_locked(as);
}
- qemu_mutex_unlock(&map_client_list_lock);
}
void cpu_exec_init_all(void)
@@ -3105,28 +3092,25 @@ void cpu_exec_init_all(void)
finalize_target_page_bits();
io_mem_init();
memory_map_init();
- qemu_mutex_init(&map_client_list_lock);
}
-void cpu_unregister_map_client(QEMUBH *bh)
+void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh)
{
- MapClient *client;
+ AddressSpaceMapClient *client;
- qemu_mutex_lock(&map_client_list_lock);
- QLIST_FOREACH(client, &map_client_list, link) {
+ QEMU_LOCK_GUARD(&as->map_client_list_lock);
+ QLIST_FOREACH(client, &as->map_client_list, link) {
if (client->bh == bh) {
- cpu_unregister_map_client_do(client);
+ address_space_unregister_map_client_do(client);
break;
}
}
- qemu_mutex_unlock(&map_client_list_lock);
}
-static void cpu_notify_map_clients(void)
+static void address_space_notify_map_clients(AddressSpace *as)
{
- qemu_mutex_lock(&map_client_list_lock);
- cpu_notify_map_clients_locked();
- qemu_mutex_unlock(&map_client_list_lock);
+ QEMU_LOCK_GUARD(&as->map_client_list_lock);
+ address_space_notify_map_clients_locked(as);
}
static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len,
@@ -3193,8 +3177,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr,
* May map a subset of the requested range, given by and returned in *plen.
* May return NULL if resources needed to perform the mapping are exhausted.
* Use only for reads OR writes - not for read-modify-write operations.
- * Use cpu_register_map_client() to know when retrying the map operation is
- * likely to succeed.
+ * Use address_space_register_map_client() to know when retrying the map
+ * operation is likely to succeed.
*/
void *address_space_map(AddressSpace *as,
hwaddr addr,
@@ -3217,25 +3201,25 @@ void *address_space_map(AddressSpace *as,
mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs);
if (!memory_access_is_direct(mr, is_write)) {
- if (qatomic_xchg(&bounce.in_use, true)) {
+ if (qatomic_xchg(&as->bounce.in_use, true)) {
*plen = 0;
return NULL;
}
/* Avoid unbounded allocations */
l = MIN(l, TARGET_PAGE_SIZE);
- bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
- bounce.addr = addr;
- bounce.len = l;
+ as->bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l);
+ as->bounce.addr = addr;
+ as->bounce.len = l;
memory_region_ref(mr);
- bounce.mr = mr;
+ as->bounce.mr = mr;
if (!is_write) {
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
- bounce.buffer, l);
+ as->bounce.buffer, l);
}
*plen = l;
- return bounce.buffer;
+ return as->bounce.buffer;
}
@@ -3243,7 +3227,7 @@ void *address_space_map(AddressSpace *as,
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
l, is_write, attrs);
fuzz_dma_read_cb(addr, *plen, mr);
- return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true);
+ return qemu_ram_ptr_length(mr->ram_block, xlat, plen, true, is_write);
}
/* Unmaps a memory region previously mapped by address_space_map().
@@ -3253,7 +3237,7 @@ void *address_space_map(AddressSpace *as,
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
bool is_write, hwaddr access_len)
{
- if (buffer != bounce.buffer) {
+ if (buffer != as->bounce.buffer) {
MemoryRegion *mr;
ram_addr_t addr1;
@@ -3269,15 +3253,15 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
return;
}
if (is_write) {
- address_space_write(as, bounce.addr, MEMTXATTRS_UNSPECIFIED,
- bounce.buffer, access_len);
+ address_space_write(as, as->bounce.addr, MEMTXATTRS_UNSPECIFIED,
+ as->bounce.buffer, access_len);
}
- qemu_vfree(bounce.buffer);
- bounce.buffer = NULL;
- memory_region_unref(bounce.mr);
+ qemu_vfree(as->bounce.buffer);
+ as->bounce.buffer = NULL;
+ memory_region_unref(as->bounce.mr);
/* Clear in_use before reading map_client_list. */
- qatomic_set_mb(&bounce.in_use, false);
- cpu_notify_map_clients();
+ qatomic_set_mb(&as->bounce.in_use, false);
+ address_space_notify_map_clients(as);
}
void *cpu_physical_memory_map(hwaddr addr,
@@ -3339,7 +3323,8 @@ int64_t address_space_cache_init(MemoryRegionCache *cache,
l = flatview_extend_translation(cache->fv, addr, len, mr,
cache->xlat, l, is_write,
MEMTXATTRS_UNSPECIFIED);
- cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true);
+ cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true,
+ is_write);
} else {
cache->ptr = NULL;
}
diff --git a/system/vl.c b/system/vl.c
index 7756eac81e..a3eede5fa5 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -773,6 +773,10 @@ static QemuOptsList qemu_run_with_opts = {
.name = "chroot",
.type = QEMU_OPT_STRING,
},
+ {
+ .name = "user",
+ .type = QEMU_OPT_STRING,
+ },
{ /* end of list */ }
},
};
@@ -2723,7 +2727,8 @@ void qmp_x_exit_preconfig(Error **errp)
if (incoming) {
Error *local_err = NULL;
if (strcmp(incoming, "defer") != 0) {
- qmp_migrate_incoming(incoming, false, NULL, &local_err);
+ qmp_migrate_incoming(incoming, false, NULL, true, true,
+ &local_err);
if (local_err) {
error_reportf_err(local_err, "-incoming %s: ", incoming);
exit(1);
@@ -3586,6 +3591,7 @@ void qemu_init(int argc, char **argv)
break;
#if defined(CONFIG_POSIX)
case QEMU_OPTION_runas:
+ warn_report("-runas is deprecated, use '-run-with user=...' instead");
if (!os_set_runas(optarg)) {
error_report("User \"%s\" doesn't exist"
" (and is not <uid>:<gid>)",
@@ -3612,6 +3618,16 @@ void qemu_init(int argc, char **argv)
if (str) {
os_set_chroot(str);
}
+ str = qemu_opt_get(opts, "user");
+ if (str) {
+ if (!os_set_runas(str)) {
+ error_report("User \"%s\" doesn't exist"
+ " (and is not <uid>:<gid>)",
+ optarg);
+ exit(1);
+ }
+ }
+
break;
}
#endif /* CONFIG_POSIX */
diff --git a/target/Kconfig b/target/Kconfig
index 5275a93ad0..7f64112e9e 100644
--- a/target/Kconfig
+++ b/target/Kconfig
@@ -17,3 +17,6 @@ source sh4/Kconfig
source sparc/Kconfig
source tricore/Kconfig
source xtensa/Kconfig
+
+config TARGET_BIG_ENDIAN
+ bool
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index 05f9ee41e9..0e2fbcb397 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -28,25 +28,37 @@
static void alpha_cpu_set_pc(CPUState *cs, vaddr value)
{
- AlphaCPU *cpu = ALPHA_CPU(cs);
-
- cpu->env.pc = value;
+ CPUAlphaState *env = cpu_env(cs);
+ env->pc = value;
}
static vaddr alpha_cpu_get_pc(CPUState *cs)
{
- AlphaCPU *cpu = ALPHA_CPU(cs);
+ CPUAlphaState *env = cpu_env(cs);
+ return env->pc;
+}
- return cpu->env.pc;
+static void alpha_cpu_synchronize_from_tb(CPUState *cs,
+ const TranslationBlock *tb)
+{
+ /* The program counter is always up to date with CF_PCREL. */
+ if (!(tb_cflags(tb) & CF_PCREL)) {
+ CPUAlphaState *env = cpu_env(cs);
+ env->pc = tb->pc;
+ }
}
static void alpha_restore_state_to_opc(CPUState *cs,
const TranslationBlock *tb,
const uint64_t *data)
{
- AlphaCPU *cpu = ALPHA_CPU(cs);
+ CPUAlphaState *env = cpu_env(cs);
- cpu->env.pc = data[0];
+ if (tb_cflags(tb) & CF_PCREL) {
+ env->pc = (env->pc & TARGET_PAGE_MASK) | data[0];
+ } else {
+ env->pc = data[0];
+ }
}
static bool alpha_cpu_has_work(CPUState *cs)
@@ -81,6 +93,11 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp)
AlphaCPUClass *acc = ALPHA_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+#ifndef CONFIG_USER_ONLY
+ /* Use pc-relative instructions in system-mode */
+ cs->tcg_cflags |= CF_PCREL;
+#endif
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -193,6 +210,7 @@ static const struct SysemuCPUOps alpha_sysemu_ops = {
static const TCGCPUOps alpha_tcg_ops = {
.initialize = alpha_translate_init,
+ .synchronize_from_tb = alpha_cpu_synchronize_from_tb,
.restore_state_to_opc = alpha_restore_state_to_opc,
#ifdef CONFIG_USER_ONLY
diff --git a/target/alpha/helper.c b/target/alpha/helper.c
index d6d4353edd..2f1000c99f 100644
--- a/target/alpha/helper.c
+++ b/target/alpha/helper.c
@@ -21,6 +21,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "fpu/softfloat-types.h"
#include "exec/helper-proto.h"
#include "qemu/qemu-print.h"
@@ -124,7 +125,7 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address,
MMUAccessType access_type,
bool maperr, uintptr_t retaddr)
{
- AlphaCPU *cpu = ALPHA_CPU(cs);
+ CPUAlphaState *env = cpu_env(cs);
target_ulong mmcsr, cause;
/* Assuming !maperr, infer the missing protection. */
@@ -155,9 +156,9 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address,
}
/* Record the arguments that PALcode would give to the kernel. */
- cpu->env.trap_arg0 = address;
- cpu->env.trap_arg1 = mmcsr;
- cpu->env.trap_arg2 = cause;
+ env->trap_arg0 = address;
+ env->trap_arg1 = mmcsr;
+ env->trap_arg2 = cause;
}
#else
/* Returns the OSF/1 entMM failure indication, or -1 on success. */
diff --git a/target/alpha/translate.c b/target/alpha/translate.c
index a97cd54f0c..fb6cac4b53 100644
--- a/target/alpha/translate.c
+++ b/target/alpha/translate.c
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/cpus.h"
-#include "disas/disas.h"
#include "qemu/host-utils.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
@@ -54,6 +53,9 @@ struct DisasContext {
uint32_t tbflags;
int mem_idx;
+ /* True if generating pc-relative code. */
+ bool pcrel;
+
/* implver and amask values for this CPU. */
int implver;
int amask;
@@ -252,6 +254,16 @@ static void st_flag_byte(TCGv val, unsigned shift)
tcg_gen_st8_i64(val, tcg_env, get_flag_ofs(shift));
}
+static void gen_pc_disp(DisasContext *ctx, TCGv dest, int32_t disp)
+{
+ uint64_t addr = ctx->base.pc_next + disp;
+ if (ctx->pcrel) {
+ tcg_gen_addi_i64(dest, cpu_pc, addr - ctx->base.pc_first);
+ } else {
+ tcg_gen_movi_i64(dest, addr);
+ }
+}
+
static void gen_excp_1(int exception, int error_code)
{
TCGv_i32 tmp1, tmp2;
@@ -263,7 +275,7 @@ static void gen_excp_1(int exception, int error_code)
static DisasJumpType gen_excp(DisasContext *ctx, int exception, int error_code)
{
- tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
+ gen_pc_disp(ctx, cpu_pc, 0);
gen_excp_1(exception, error_code);
return DISAS_NORETURN;
}
@@ -425,60 +437,49 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb,
return DISAS_NEXT;
}
-static bool use_goto_tb(DisasContext *ctx, uint64_t dest)
+static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp)
{
- return translator_use_goto_tb(&ctx->base, dest);
+ if (translator_use_goto_tb(&ctx->base, ctx->base.pc_next + disp)) {
+ /* With PCREL, PC must always be up-to-date. */
+ if (ctx->pcrel) {
+ gen_pc_disp(ctx, cpu_pc, disp);
+ tcg_gen_goto_tb(idx);
+ } else {
+ tcg_gen_goto_tb(idx);
+ gen_pc_disp(ctx, cpu_pc, disp);
+ }
+ tcg_gen_exit_tb(ctx->base.tb, idx);
+ } else {
+ gen_pc_disp(ctx, cpu_pc, disp);
+ tcg_gen_lookup_and_goto_ptr();
+ }
}
static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp)
{
- uint64_t dest = ctx->base.pc_next + (disp << 2);
-
if (ra != 31) {
- tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next);
+ gen_pc_disp(ctx, ctx->ir[ra], 0);
}
/* Notice branch-to-next; used to initialize RA with the PC. */
if (disp == 0) {
- return 0;
- } else if (use_goto_tb(ctx, dest)) {
- tcg_gen_goto_tb(0);
- tcg_gen_movi_i64(cpu_pc, dest);
- tcg_gen_exit_tb(ctx->base.tb, 0);
- return DISAS_NORETURN;
- } else {
- tcg_gen_movi_i64(cpu_pc, dest);
- return DISAS_PC_UPDATED;
+ return DISAS_NEXT;
}
+ gen_goto_tb(ctx, 0, disp);
+ return DISAS_NORETURN;
}
static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond,
TCGv cmp, uint64_t imm, int32_t disp)
{
- uint64_t dest = ctx->base.pc_next + (disp << 2);
TCGLabel *lab_true = gen_new_label();
- if (use_goto_tb(ctx, dest)) {
- tcg_gen_brcondi_i64(cond, cmp, imm, lab_true);
-
- tcg_gen_goto_tb(0);
- tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
- tcg_gen_exit_tb(ctx->base.tb, 0);
+ tcg_gen_brcondi_i64(cond, cmp, imm, lab_true);
+ gen_goto_tb(ctx, 0, 0);
+ gen_set_label(lab_true);
+ gen_goto_tb(ctx, 1, disp);
- gen_set_label(lab_true);
- tcg_gen_goto_tb(1);
- tcg_gen_movi_i64(cpu_pc, dest);
- tcg_gen_exit_tb(ctx->base.tb, 1);
-
- return DISAS_NORETURN;
- } else {
- TCGv_i64 i = tcg_constant_i64(imm);
- TCGv_i64 d = tcg_constant_i64(dest);
- TCGv_i64 p = tcg_constant_i64(ctx->base.pc_next);
-
- tcg_gen_movcond_i64(cond, cpu_pc, cmp, i, d, p);
- return DISAS_PC_UPDATED;
- }
+ return DISAS_NORETURN;
}
static DisasJumpType gen_bcond(DisasContext *ctx, TCGCond cond, int ra,
@@ -1106,7 +1107,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode)
}
/* Allow interrupts to be recognized right away. */
- tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
+ gen_pc_disp(ctx, cpu_pc, 0);
return DISAS_PC_UPDATED_NOCHAIN;
case 0x36:
@@ -1153,19 +1154,17 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode)
#else
{
TCGv tmp = tcg_temp_new();
- uint64_t exc_addr = ctx->base.pc_next;
- uint64_t entry = ctx->palbr;
+ uint64_t entry;
+ gen_pc_disp(ctx, tmp, 0);
if (ctx->tbflags & ENV_FLAG_PAL_MODE) {
- exc_addr |= 1;
+ tcg_gen_ori_i64(tmp, tmp, 1);
} else {
- tcg_gen_movi_i64(tmp, 1);
- st_flag_byte(tmp, ENV_FLAG_PAL_SHIFT);
+ st_flag_byte(tcg_constant_i64(1), ENV_FLAG_PAL_SHIFT);
}
-
- tcg_gen_movi_i64(tmp, exc_addr);
tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUAlphaState, exc_addr));
+ entry = ctx->palbr;
entry += (palcode & 0x80
? 0x2000 + (palcode - 0x80) * 64
: 0x1000 + palcode * 64);
@@ -1382,7 +1381,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn)
real_islit = islit = extract32(insn, 12, 1);
lit = extract32(insn, 13, 8);
- disp21 = sextract32(insn, 0, 21);
+ disp21 = sextract32(insn, 0, 21) * 4;
disp16 = sextract32(insn, 0, 16);
disp12 = sextract32(insn, 0, 12);
@@ -2359,9 +2358,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn)
/* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch
prediction stack action, which of course we don't implement. */
vb = load_gpr(ctx, rb);
- tcg_gen_andi_i64(cpu_pc, vb, ~3);
if (ra != 31) {
- tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next);
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i64(tmp, vb, ~3);
+ gen_pc_disp(ctx, ctx->ir[ra], 0);
+ tcg_gen_mov_i64(cpu_pc, tmp);
+ } else {
+ tcg_gen_andi_i64(cpu_pc, vb, ~3);
}
ret = DISAS_PC_UPDATED;
break;
@@ -2862,6 +2865,7 @@ static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
ctx->tbflags = ctx->base.tb->flags;
ctx->mem_idx = alpha_env_mmu_index(env);
+ ctx->pcrel = ctx->base.tb->cflags & CF_PCREL;
ctx->implver = env->implver;
ctx->amask = env->amask;
@@ -2897,7 +2901,13 @@ static void alpha_tr_tb_start(DisasContextBase *db, CPUState *cpu)
static void alpha_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
{
- tcg_gen_insn_start(dcbase->pc_next);
+ DisasContext *ctx = container_of(dcbase, DisasContext, base);
+
+ if (ctx->pcrel) {
+ tcg_gen_insn_start(dcbase->pc_next & ~TARGET_PAGE_MASK);
+ } else {
+ tcg_gen_insn_start(dcbase->pc_next);
+ }
}
static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
@@ -2920,14 +2930,10 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
case DISAS_NORETURN:
break;
case DISAS_TOO_MANY:
- if (use_goto_tb(ctx, ctx->base.pc_next)) {
- tcg_gen_goto_tb(0);
- tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
- tcg_gen_exit_tb(ctx->base.tb, 0);
- }
- /* FALLTHRU */
+ gen_goto_tb(ctx, 0, 0);
+ break;
case DISAS_PC_STALE:
- tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next);
+ gen_pc_disp(ctx, cpu_pc, 0);
/* FALLTHRU */
case DISAS_PC_UPDATED:
tcg_gen_lookup_and_goto_ptr();
@@ -2940,20 +2946,12 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void alpha_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps alpha_tr_ops = {
.init_disas_context = alpha_tr_init_disas_context,
.tb_start = alpha_tr_tb_start,
.insn_start = alpha_tr_insn_start,
.translate_insn = alpha_tr_translate_insn,
.tb_stop = alpha_tr_tb_stop,
- .disas_log = alpha_tr_disas_log,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
diff --git a/target/arm/Kconfig b/target/arm/Kconfig
index bf57d739cd..7f8a2217ae 100644
--- a/target/arm/Kconfig
+++ b/target/arm/Kconfig
@@ -6,6 +6,10 @@ config ARM
# translate.c v7m helpers under ARM_V7M.
select ARM_V7M if TCG
+ select DEVICE_TREE # needed by boot.c
+
config AARCH64
bool
select ARM
+ # kvm_arch_fixup_msi_route() needs to access PCIDevice
+ select PCI if KVM
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index fdc3eda318..77f8c9c748 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1941,7 +1941,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
/* Use pc-relative instructions in system-mode */
- cs->tcg_cflags |= CF_PCREL;
+ tcg_cflags_set(cs, CF_PCREL);
#endif
/* If we needed to query the host kernel for the CPU features
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index a550bcd25f..c17264c239 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -26,6 +26,7 @@
#include "cpu-qom.h"
#include "exec/cpu-defs.h"
#include "exec/gdbstub.h"
+#include "exec/page-protection.h"
#include "qapi/qapi-types-common.h"
#include "target/arm/multiprocessing.h"
#include "target/arm/gtimer.h"
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 2b02733305..f830531dd3 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -132,12 +132,6 @@ DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr)
DEF_HELPER_3(vfp_minnumh, f16, f16, f16, ptr)
DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr)
DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr)
-DEF_HELPER_1(vfp_negh, f16, f16)
-DEF_HELPER_1(vfp_negs, f32, f32)
-DEF_HELPER_1(vfp_negd, f64, f64)
-DEF_HELPER_1(vfp_absh, f16, f16)
-DEF_HELPER_1(vfp_abss, f32, f32)
-DEF_HELPER_1(vfp_absd, f64, f64)
DEF_HELPER_2(vfp_sqrth, f16, f16, env)
DEF_HELPER_2(vfp_sqrts, f32, f32, env)
DEF_HELPER_2(vfp_sqrtd, f64, f64, env)
@@ -360,8 +354,6 @@ DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64)
DEF_HELPER_2(neon_add_u8, i32, i32, i32)
DEF_HELPER_2(neon_add_u16, i32, i32, i32)
-DEF_HELPER_2(neon_padd_u8, i32, i32, i32)
-DEF_HELPER_2(neon_padd_u16, i32, i32, i32)
DEF_HELPER_2(neon_sub_u8, i32, i32, i32)
DEF_HELPER_2(neon_sub_u16, i32, i32, i32)
DEF_HELPER_2(neon_mul_u8, i32, i32, i32)
@@ -656,13 +648,6 @@ DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG,
DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_paddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_pmaxh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_pminh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_padds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_pmaxs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-DEF_HELPER_FLAGS_5(neon_pmins, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
-
DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
@@ -730,33 +715,43 @@ DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
@@ -772,9 +767,11 @@ DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, i32)
@@ -1042,6 +1039,47 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG,
DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG,
void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_4(gvec_smaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_smaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_smaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_4(gvec_sminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_sminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_sminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_4(gvec_umaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_umaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_umaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
+
#ifdef TARGET_AARCH64
#include "tcg/helper-a64.h"
#include "tcg/helper-sve.h"
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 08d0757438..45e2218be5 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -396,85 +396,85 @@ struct hvf_sreg_match {
};
static struct hvf_sreg_match hvf_sreg_match[] = {
- { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 7) },
-
- { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 4) },
- { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 5) },
- { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 6) },
- { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 7) },
+ { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 7) },
+
+ { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 4) },
+ { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 5) },
+ { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 6) },
+ { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 7) },
#ifdef SYNC_NO_RAW_REGS
/*
@@ -486,7 +486,7 @@ static struct hvf_sreg_match hvf_sreg_match[] = {
{ HV_SYS_REG_MPIDR_EL1, HVF_SYSREG(0, 0, 3, 0, 5) },
{ HV_SYS_REG_ID_AA64PFR0_EL1, HVF_SYSREG(0, 4, 3, 0, 0) },
#endif
- { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 2) },
+ { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 1) },
{ HV_SYS_REG_ID_AA64DFR0_EL1, HVF_SYSREG(0, 5, 3, 0, 0) },
{ HV_SYS_REG_ID_AA64DFR1_EL1, HVF_SYSREG(0, 5, 3, 0, 1) },
{ HV_SYS_REG_ID_AA64ISAR0_EL1, HVF_SYSREG(0, 6, 3, 0, 0) },
diff --git a/target/arm/ptw.c b/target/arm/ptw.c
index 31ae43f60e..4476b32ff5 100644
--- a/target/arm/ptw.c
+++ b/target/arm/ptw.c
@@ -11,6 +11,7 @@
#include "qemu/range.h"
#include "qemu/main-loop.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "cpu.h"
#include "internals.h"
#include "cpu-features.h"
diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode
index 0e7656fd15..f48adef5bb 100644
--- a/target/arm/tcg/a64.decode
+++ b/target/arm/tcg/a64.decode
@@ -19,11 +19,53 @@
# This file is processed by scripts/decodetree.py
#
-&r rn
-&ri rd imm
-&rri_sf rd rn imm sf
-&i imm
-
+%rd 0:5
+%esz_sd 22:1 !function=plus_2
+%esz_hsd 22:2 !function=xor_2
+%hl 11:1 21:1
+%hlm 11:1 20:2
+
+&r rn
+&ri rd imm
+&rri_sf rd rn imm sf
+&i imm
+&rr_e rd rn esz
+&rrr_e rd rn rm esz
+&rrx_e rd rn rm idx esz
+&qrr_e q rd rn esz
+&qrrr_e q rd rn rm esz
+&qrrx_e q rd rn rm idx esz
+&qrrrr_e q rd rn rm ra esz
+
+@rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1
+@rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3
+@rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd
+
+@rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1
+@rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd
+@rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd
+
+@rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm
+@rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl
+@rrx_d ........ .. . rm:5 .... idx:1 . rn:5 rd:5 &rrx_e esz=3
+
+@rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0
+@r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0
+@rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0
+@rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3
+@rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3
+
+@qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0
+@qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1
+@qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd
+@qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e
+
+@qrrx_h . q:1 .. .... .. .. rm:4 .... . . rn:5 rd:5 \
+ &qrrx_e esz=1 idx=%hlm
+@qrrx_s . q:1 .. .... .. . rm:5 .... . . rn:5 rd:5 \
+ &qrrx_e esz=2 idx=%hl
+@qrrx_d . q:1 .. .... .. . rm:5 .... idx:1 . rn:5 rd:5 \
+ &qrrx_e esz=3
### Data Processing - Immediate
@@ -590,3 +632,268 @@ CPYFE 00 011 0 01100 ..... .... 01 ..... ..... @cpy
CPYP 00 011 1 01000 ..... .... 01 ..... ..... @cpy
CPYM 00 011 1 01010 ..... .... 01 ..... ..... @cpy
CPYE 00 011 1 01100 ..... .... 01 ..... ..... @cpy
+
+### Cryptographic AES
+
+AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0
+AESD 01001110 00 10100 00101 10 ..... ..... @r2r_q1e0
+AESMC 01001110 00 10100 00110 10 ..... ..... @rr_q1e0
+AESIMC 01001110 00 10100 00111 10 ..... ..... @rr_q1e0
+
+### Cryptographic three-register SHA
+
+SHA1C 0101 1110 000 ..... 000000 ..... ..... @rrr_q1e0
+SHA1P 0101 1110 000 ..... 000100 ..... ..... @rrr_q1e0
+SHA1M 0101 1110 000 ..... 001000 ..... ..... @rrr_q1e0
+SHA1SU0 0101 1110 000 ..... 001100 ..... ..... @rrr_q1e0
+SHA256H 0101 1110 000 ..... 010000 ..... ..... @rrr_q1e0
+SHA256H2 0101 1110 000 ..... 010100 ..... ..... @rrr_q1e0
+SHA256SU1 0101 1110 000 ..... 011000 ..... ..... @rrr_q1e0
+
+### Cryptographic two-register SHA
+
+SHA1H 0101 1110 0010 1000 0000 10 ..... ..... @rr_q1e0
+SHA1SU1 0101 1110 0010 1000 0001 10 ..... ..... @rr_q1e0
+SHA256SU0 0101 1110 0010 1000 0010 10 ..... ..... @rr_q1e0
+
+### Cryptographic three-register SHA512
+
+SHA512H 1100 1110 011 ..... 100000 ..... ..... @rrr_q1e0
+SHA512H2 1100 1110 011 ..... 100001 ..... ..... @rrr_q1e0
+SHA512SU1 1100 1110 011 ..... 100010 ..... ..... @rrr_q1e0
+RAX1 1100 1110 011 ..... 100011 ..... ..... @rrr_q1e3
+SM3PARTW1 1100 1110 011 ..... 110000 ..... ..... @rrr_q1e0
+SM3PARTW2 1100 1110 011 ..... 110001 ..... ..... @rrr_q1e0
+SM4EKEY 1100 1110 011 ..... 110010 ..... ..... @rrr_q1e0
+
+### Cryptographic two-register SHA512
+
+SHA512SU0 1100 1110 110 00000 100000 ..... ..... @rr_q1e0
+SM4E 1100 1110 110 00000 100001 ..... ..... @r2r_q1e0
+
+### Cryptographic four-register
+
+EOR3 1100 1110 000 ..... 0 ..... ..... ..... @rrrr_q1e3
+BCAX 1100 1110 001 ..... 0 ..... ..... ..... @rrrr_q1e3
+SM3SS1 1100 1110 010 ..... 0 ..... ..... ..... @rrrr_q1e3
+
+### Cryptographic three-register, imm2
+
+&crypto3i rd rn rm imm
+@crypto3i ........ ... rm:5 .. imm:2 .. rn:5 rd:5 &crypto3i
+
+SM3TT1A 11001110 010 ..... 10 .. 00 ..... ..... @crypto3i
+SM3TT1B 11001110 010 ..... 10 .. 01 ..... ..... @crypto3i
+SM3TT2A 11001110 010 ..... 10 .. 10 ..... ..... @crypto3i
+SM3TT2B 11001110 010 ..... 10 .. 11 ..... ..... @crypto3i
+
+### Cryptographic XAR
+
+XAR 1100 1110 100 rm:5 imm:6 rn:5 rd:5
+
+### Advanced SIMD scalar copy
+
+DUP_element_s 0101 1110 000 imm:5 0 0000 1 rn:5 rd:5
+
+### Advanced SIMD copy
+
+DUP_element_v 0 q:1 00 1110 000 imm:5 0 0000 1 rn:5 rd:5
+DUP_general 0 q:1 00 1110 000 imm:5 0 0001 1 rn:5 rd:5
+INS_general 0 1 00 1110 000 imm:5 0 0011 1 rn:5 rd:5
+SMOV 0 q:1 00 1110 000 imm:5 0 0101 1 rn:5 rd:5
+UMOV 0 q:1 00 1110 000 imm:5 0 0111 1 rn:5 rd:5
+INS_element 0 1 10 1110 000 di:5 0 si:4 1 rn:5 rd:5
+
+### Advanced SIMD scalar three same
+
+FADD_s 0001 1110 ..1 ..... 0010 10 ..... ..... @rrr_hsd
+FSUB_s 0001 1110 ..1 ..... 0011 10 ..... ..... @rrr_hsd
+FDIV_s 0001 1110 ..1 ..... 0001 10 ..... ..... @rrr_hsd
+FMUL_s 0001 1110 ..1 ..... 0000 10 ..... ..... @rrr_hsd
+FNMUL_s 0001 1110 ..1 ..... 1000 10 ..... ..... @rrr_hsd
+
+FMAX_s 0001 1110 ..1 ..... 0100 10 ..... ..... @rrr_hsd
+FMIN_s 0001 1110 ..1 ..... 0101 10 ..... ..... @rrr_hsd
+FMAXNM_s 0001 1110 ..1 ..... 0110 10 ..... ..... @rrr_hsd
+FMINNM_s 0001 1110 ..1 ..... 0111 10 ..... ..... @rrr_hsd
+
+FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h
+FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd
+
+FCMEQ_s 0101 1110 010 ..... 00100 1 ..... ..... @rrr_h
+FCMEQ_s 0101 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd
+
+FCMGE_s 0111 1110 010 ..... 00100 1 ..... ..... @rrr_h
+FCMGE_s 0111 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd
+
+FCMGT_s 0111 1110 110 ..... 00100 1 ..... ..... @rrr_h
+FCMGT_s 0111 1110 1.1 ..... 11100 1 ..... ..... @rrr_sd
+
+FACGE_s 0111 1110 010 ..... 00101 1 ..... ..... @rrr_h
+FACGE_s 0111 1110 0.1 ..... 11101 1 ..... ..... @rrr_sd
+
+FACGT_s 0111 1110 110 ..... 00101 1 ..... ..... @rrr_h
+FACGT_s 0111 1110 1.1 ..... 11101 1 ..... ..... @rrr_sd
+
+FABD_s 0111 1110 110 ..... 00010 1 ..... ..... @rrr_h
+FABD_s 0111 1110 1.1 ..... 11010 1 ..... ..... @rrr_sd
+
+FRECPS_s 0101 1110 010 ..... 00111 1 ..... ..... @rrr_h
+FRECPS_s 0101 1110 0.1 ..... 11111 1 ..... ..... @rrr_sd
+
+FRSQRTS_s 0101 1110 110 ..... 00111 1 ..... ..... @rrr_h
+FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd
+
+### Advanced SIMD scalar pairwise
+
+FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h
+FADDP_s 0111 1110 0.11 0000 1101 10 ..... ..... @rr_sd
+
+FMAXP_s 0101 1110 0011 0000 1111 10 ..... ..... @rr_h
+FMAXP_s 0111 1110 0.11 0000 1111 10 ..... ..... @rr_sd
+
+FMINP_s 0101 1110 1011 0000 1111 10 ..... ..... @rr_h
+FMINP_s 0111 1110 1.11 0000 1111 10 ..... ..... @rr_sd
+
+FMAXNMP_s 0101 1110 0011 0000 1100 10 ..... ..... @rr_h
+FMAXNMP_s 0111 1110 0.11 0000 1100 10 ..... ..... @rr_sd
+
+FMINNMP_s 0101 1110 1011 0000 1100 10 ..... ..... @rr_h
+FMINNMP_s 0111 1110 1.11 0000 1100 10 ..... ..... @rr_sd
+
+ADDP_s 0101 1110 1111 0001 1011 10 ..... ..... @rr_d
+
+### Advanced SIMD three same
+
+FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h
+FADD_v 0.00 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd
+
+FSUB_v 0.00 1110 110 ..... 00010 1 ..... ..... @qrrr_h
+FSUB_v 0.00 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd
+
+FDIV_v 0.10 1110 010 ..... 00111 1 ..... ..... @qrrr_h
+FDIV_v 0.10 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd
+
+FMUL_v 0.10 1110 010 ..... 00011 1 ..... ..... @qrrr_h
+FMUL_v 0.10 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd
+
+FMAX_v 0.00 1110 010 ..... 00110 1 ..... ..... @qrrr_h
+FMAX_v 0.00 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd
+
+FMIN_v 0.00 1110 110 ..... 00110 1 ..... ..... @qrrr_h
+FMIN_v 0.00 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd
+
+FMAXNM_v 0.00 1110 010 ..... 00000 1 ..... ..... @qrrr_h
+FMAXNM_v 0.00 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd
+
+FMINNM_v 0.00 1110 110 ..... 00000 1 ..... ..... @qrrr_h
+FMINNM_v 0.00 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd
+
+FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h
+FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd
+
+FMLA_v 0.00 1110 010 ..... 00001 1 ..... ..... @qrrr_h
+FMLA_v 0.00 1110 0.1 ..... 11001 1 ..... ..... @qrrr_sd
+
+FMLS_v 0.00 1110 110 ..... 00001 1 ..... ..... @qrrr_h
+FMLS_v 0.00 1110 1.1 ..... 11001 1 ..... ..... @qrrr_sd
+
+FMLAL_v 0.00 1110 001 ..... 11101 1 ..... ..... @qrrr_h
+FMLSL_v 0.00 1110 101 ..... 11101 1 ..... ..... @qrrr_h
+FMLAL2_v 0.10 1110 001 ..... 11001 1 ..... ..... @qrrr_h
+FMLSL2_v 0.10 1110 101 ..... 11001 1 ..... ..... @qrrr_h
+
+FCMEQ_v 0.00 1110 010 ..... 00100 1 ..... ..... @qrrr_h
+FCMEQ_v 0.00 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd
+
+FCMGE_v 0.10 1110 010 ..... 00100 1 ..... ..... @qrrr_h
+FCMGE_v 0.10 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd
+
+FCMGT_v 0.10 1110 110 ..... 00100 1 ..... ..... @qrrr_h
+FCMGT_v 0.10 1110 1.1 ..... 11100 1 ..... ..... @qrrr_sd
+
+FACGE_v 0.10 1110 010 ..... 00101 1 ..... ..... @qrrr_h
+FACGE_v 0.10 1110 0.1 ..... 11101 1 ..... ..... @qrrr_sd
+
+FACGT_v 0.10 1110 110 ..... 00101 1 ..... ..... @qrrr_h
+FACGT_v 0.10 1110 1.1 ..... 11101 1 ..... ..... @qrrr_sd
+
+FABD_v 0.10 1110 110 ..... 00010 1 ..... ..... @qrrr_h
+FABD_v 0.10 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd
+
+FRECPS_v 0.00 1110 010 ..... 00111 1 ..... ..... @qrrr_h
+FRECPS_v 0.00 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd
+
+FRSQRTS_v 0.00 1110 110 ..... 00111 1 ..... ..... @qrrr_h
+FRSQRTS_v 0.00 1110 1.1 ..... 11111 1 ..... ..... @qrrr_sd
+
+FADDP_v 0.10 1110 010 ..... 00010 1 ..... ..... @qrrr_h
+FADDP_v 0.10 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd
+
+FMAXP_v 0.10 1110 010 ..... 00110 1 ..... ..... @qrrr_h
+FMAXP_v 0.10 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd
+
+FMINP_v 0.10 1110 110 ..... 00110 1 ..... ..... @qrrr_h
+FMINP_v 0.10 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd
+
+FMAXNMP_v 0.10 1110 010 ..... 00000 1 ..... ..... @qrrr_h
+FMAXNMP_v 0.10 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd
+
+FMINNMP_v 0.10 1110 110 ..... 00000 1 ..... ..... @qrrr_h
+FMINNMP_v 0.10 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd
+
+ADDP_v 0.00 1110 ..1 ..... 10111 1 ..... ..... @qrrr_e
+SMAXP_v 0.00 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e
+SMINP_v 0.00 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e
+UMAXP_v 0.10 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e
+UMINP_v 0.10 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e
+
+AND_v 0.00 1110 001 ..... 00011 1 ..... ..... @qrrr_b
+BIC_v 0.00 1110 011 ..... 00011 1 ..... ..... @qrrr_b
+ORR_v 0.00 1110 101 ..... 00011 1 ..... ..... @qrrr_b
+ORN_v 0.00 1110 111 ..... 00011 1 ..... ..... @qrrr_b
+EOR_v 0.10 1110 001 ..... 00011 1 ..... ..... @qrrr_b
+BSL_v 0.10 1110 011 ..... 00011 1 ..... ..... @qrrr_b
+BIT_v 0.10 1110 101 ..... 00011 1 ..... ..... @qrrr_b
+BIF_v 0.10 1110 111 ..... 00011 1 ..... ..... @qrrr_b
+
+### Advanced SIMD scalar x indexed element
+
+FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h
+FMUL_si 0101 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s
+FMUL_si 0101 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d
+
+FMLA_si 0101 1111 00 .. .... 0001 . 0 ..... ..... @rrx_h
+FMLA_si 0101 1111 10 .. .... 0001 . 0 ..... ..... @rrx_s
+FMLA_si 0101 1111 11 0. .... 0001 . 0 ..... ..... @rrx_d
+
+FMLS_si 0101 1111 00 .. .... 0101 . 0 ..... ..... @rrx_h
+FMLS_si 0101 1111 10 .. .... 0101 . 0 ..... ..... @rrx_s
+FMLS_si 0101 1111 11 0. .... 0101 . 0 ..... ..... @rrx_d
+
+FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h
+FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s
+FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d
+
+### Advanced SIMD vector x indexed element
+
+FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h
+FMUL_vi 0.00 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s
+FMUL_vi 0.00 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d
+
+FMLA_vi 0.00 1111 00 .. .... 0001 . 0 ..... ..... @qrrx_h
+FMLA_vi 0.00 1111 10 . ..... 0001 . 0 ..... ..... @qrrx_s
+FMLA_vi 0.00 1111 11 0 ..... 0001 . 0 ..... ..... @qrrx_d
+
+FMLS_vi 0.00 1111 00 .. .... 0101 . 0 ..... ..... @qrrx_h
+FMLS_vi 0.00 1111 10 . ..... 0101 . 0 ..... ..... @qrrx_s
+FMLS_vi 0.00 1111 11 0 ..... 0101 . 0 ..... ..... @qrrx_d
+
+FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h
+FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s
+FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d
+
+FMLAL_vi 0.00 1111 10 .. .... 0000 . 0 ..... ..... @qrrx_h
+FMLSL_vi 0.00 1111 10 .. .... 0100 . 0 ..... ..... @qrrx_h
+FMLAL2_vi 0.10 1111 10 .. .... 1000 . 0 ..... ..... @qrrx_h
+FMLSL2_vi 0.10 1111 10 .. .... 1100 . 0 ..... ..... @qrrx_h
diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c
new file mode 100644
index 0000000000..22c9d17dce
--- /dev/null
+++ b/target/arm/tcg/gengvec.c
@@ -0,0 +1,1672 @@
+/*
+ * ARM generic vector expansion
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2005-2007 CodeSourcery
+ * Copyright (c) 2007 OpenedHand, Ltd.
+ *
+ * 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.1 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 "qemu/osdep.h"
+#include "translate.h"
+
+
+static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs,
+ uint32_t opr_sz, uint32_t max_sz,
+ gen_helper_gvec_3_ptr *fn)
+{
+ TCGv_ptr qc_ptr = tcg_temp_new_ptr();
+
+ tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc));
+ tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr,
+ opr_sz, max_sz, 0, fn);
+}
+
+void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3_ptr * const fns[2] = {
+ gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32
+ };
+ tcg_debug_assert(vece >= 1 && vece <= 2);
+ gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]);
+}
+
+void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3_ptr * const fns[2] = {
+ gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32
+ };
+ tcg_debug_assert(vece >= 1 && vece <= 2);
+ gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]);
+}
+
+#define GEN_CMP0(NAME, COND) \
+ void NAME(unsigned vece, uint32_t d, uint32_t m, \
+ uint32_t opr_sz, uint32_t max_sz) \
+ { tcg_gen_gvec_cmpi(COND, vece, d, m, 0, opr_sz, max_sz); }
+
+GEN_CMP0(gen_gvec_ceq0, TCG_COND_EQ)
+GEN_CMP0(gen_gvec_cle0, TCG_COND_LE)
+GEN_CMP0(gen_gvec_cge0, TCG_COND_GE)
+GEN_CMP0(gen_gvec_clt0, TCG_COND_LT)
+GEN_CMP0(gen_gvec_cgt0, TCG_COND_GT)
+
+#undef GEN_CMP0
+
+static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_vec_sar8i_i64(a, a, shift);
+ tcg_gen_vec_add8_i64(d, d, a);
+}
+
+static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_vec_sar16i_i64(a, a, shift);
+ tcg_gen_vec_add16_i64(d, d, a);
+}
+
+static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
+{
+ tcg_gen_sari_i32(a, a, shift);
+ tcg_gen_add_i32(d, d, a);
+}
+
+static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_sari_i64(a, a, shift);
+ tcg_gen_add_i64(d, d, a);
+}
+
+static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ tcg_gen_sari_vec(vece, a, a, sh);
+ tcg_gen_add_vec(vece, d, d, a);
+}
+
+void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sari_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_ssra8_i64,
+ .fniv = gen_ssra_vec,
+ .fno = gen_helper_gvec_ssra_b,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni8 = gen_ssra16_i64,
+ .fniv = gen_ssra_vec,
+ .fno = gen_helper_gvec_ssra_h,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_ssra32_i32,
+ .fniv = gen_ssra_vec,
+ .fno = gen_helper_gvec_ssra_s,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_ssra64_i64,
+ .fniv = gen_ssra_vec,
+ .fno = gen_helper_gvec_ssra_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize]. */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ /*
+ * Shifts larger than the element size are architecturally valid.
+ * Signed results in all sign bits.
+ */
+ shift = MIN(shift, (8 << vece) - 1);
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+}
+
+static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_vec_shr8i_i64(a, a, shift);
+ tcg_gen_vec_add8_i64(d, d, a);
+}
+
+static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_vec_shr16i_i64(a, a, shift);
+ tcg_gen_vec_add16_i64(d, d, a);
+}
+
+static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
+{
+ tcg_gen_shri_i32(a, a, shift);
+ tcg_gen_add_i32(d, d, a);
+}
+
+static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_shri_i64(a, a, shift);
+ tcg_gen_add_i64(d, d, a);
+}
+
+static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ tcg_gen_shri_vec(vece, a, a, sh);
+ tcg_gen_add_vec(vece, d, d, a);
+}
+
+void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_shri_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_usra8_i64,
+ .fniv = gen_usra_vec,
+ .fno = gen_helper_gvec_usra_b,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8, },
+ { .fni8 = gen_usra16_i64,
+ .fniv = gen_usra_vec,
+ .fno = gen_helper_gvec_usra_h,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16, },
+ { .fni4 = gen_usra32_i32,
+ .fniv = gen_usra_vec,
+ .fno = gen_helper_gvec_usra_s,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32, },
+ { .fni8 = gen_usra64_i64,
+ .fniv = gen_usra_vec,
+ .fno = gen_helper_gvec_usra_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64, },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize]. */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ /*
+ * Shifts larger than the element size are architecturally valid.
+ * Unsigned results in all zeros as input to accumulate: nop.
+ */
+ if (shift < (8 << vece)) {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ } else {
+ /* Nop, but we do need to clear the tail. */
+ tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
+ }
+}
+
+/*
+ * Shift one less than the requested amount, and the low bit is
+ * the rounding bit. For the 8 and 16-bit operations, because we
+ * mask the low bit, we can perform a normal integer shift instead
+ * of a vector shift.
+ */
+static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, sh - 1);
+ tcg_gen_andi_i64(t, t, dup_const(MO_8, 1));
+ tcg_gen_vec_sar8i_i64(d, a, sh);
+ tcg_gen_vec_add8_i64(d, d, t);
+}
+
+static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, sh - 1);
+ tcg_gen_andi_i64(t, t, dup_const(MO_16, 1));
+ tcg_gen_vec_sar16i_i64(d, a, sh);
+ tcg_gen_vec_add16_i64(d, d, t);
+}
+
+void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
+{
+ TCGv_i32 t;
+
+ /* Handle shift by the input size for the benefit of trans_SRSHR_ri */
+ if (sh == 32) {
+ tcg_gen_movi_i32(d, 0);
+ return;
+ }
+ t = tcg_temp_new_i32();
+ tcg_gen_extract_i32(t, a, sh - 1, 1);
+ tcg_gen_sari_i32(d, a, sh);
+ tcg_gen_add_i32(d, d, t);
+}
+
+ void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_extract_i64(t, a, sh - 1, 1);
+ tcg_gen_sari_i64(d, a, sh);
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ TCGv_vec ones = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_shri_vec(vece, t, a, sh - 1);
+ tcg_gen_dupi_vec(vece, ones, 1);
+ tcg_gen_and_vec(vece, t, t, ones);
+ tcg_gen_sari_vec(vece, d, a, sh);
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_srshr8_i64,
+ .fniv = gen_srshr_vec,
+ .fno = gen_helper_gvec_srshr_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni8 = gen_srshr16_i64,
+ .fniv = gen_srshr_vec,
+ .fno = gen_helper_gvec_srshr_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_srshr32_i32,
+ .fniv = gen_srshr_vec,
+ .fno = gen_helper_gvec_srshr_s,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_srshr64_i64,
+ .fniv = gen_srshr_vec,
+ .fno = gen_helper_gvec_srshr_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize] */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ if (shift == (8 << vece)) {
+ /*
+ * Shifts larger than the element size are architecturally valid.
+ * Signed results in all sign bits. With rounding, this produces
+ * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0.
+ * I.e. always zero.
+ */
+ tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0);
+ } else {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ }
+}
+
+static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ gen_srshr8_i64(t, a, sh);
+ tcg_gen_vec_add8_i64(d, d, t);
+}
+
+static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ gen_srshr16_i64(t, a, sh);
+ tcg_gen_vec_add16_i64(d, d, t);
+}
+
+static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+
+ gen_srshr32_i32(t, a, sh);
+ tcg_gen_add_i32(d, d, t);
+}
+
+static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ gen_srshr64_i64(t, a, sh);
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+
+ gen_srshr_vec(vece, t, a, sh);
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_srsra8_i64,
+ .fniv = gen_srsra_vec,
+ .fno = gen_helper_gvec_srsra_b,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_8 },
+ { .fni8 = gen_srsra16_i64,
+ .fniv = gen_srsra_vec,
+ .fno = gen_helper_gvec_srsra_h,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_16 },
+ { .fni4 = gen_srsra32_i32,
+ .fniv = gen_srsra_vec,
+ .fno = gen_helper_gvec_srsra_s,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_32 },
+ { .fni8 = gen_srsra64_i64,
+ .fniv = gen_srsra_vec,
+ .fno = gen_helper_gvec_srsra_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize] */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ /*
+ * Shifts larger than the element size are architecturally valid.
+ * Signed results in all sign bits. With rounding, this produces
+ * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0.
+ * I.e. always zero. With accumulation, this leaves D unchanged.
+ */
+ if (shift == (8 << vece)) {
+ /* Nop, but we do need to clear the tail. */
+ tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
+ } else {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ }
+}
+
+static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, sh - 1);
+ tcg_gen_andi_i64(t, t, dup_const(MO_8, 1));
+ tcg_gen_vec_shr8i_i64(d, a, sh);
+ tcg_gen_vec_add8_i64(d, d, t);
+}
+
+static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, sh - 1);
+ tcg_gen_andi_i64(t, t, dup_const(MO_16, 1));
+ tcg_gen_vec_shr16i_i64(d, a, sh);
+ tcg_gen_vec_add16_i64(d, d, t);
+}
+
+void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
+{
+ TCGv_i32 t;
+
+ /* Handle shift by the input size for the benefit of trans_URSHR_ri */
+ if (sh == 32) {
+ tcg_gen_extract_i32(d, a, sh - 1, 1);
+ return;
+ }
+ t = tcg_temp_new_i32();
+ tcg_gen_extract_i32(t, a, sh - 1, 1);
+ tcg_gen_shri_i32(d, a, sh);
+ tcg_gen_add_i32(d, d, t);
+}
+
+void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_extract_i64(t, a, sh - 1, 1);
+ tcg_gen_shri_i64(d, a, sh);
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ TCGv_vec ones = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_shri_vec(vece, t, a, shift - 1);
+ tcg_gen_dupi_vec(vece, ones, 1);
+ tcg_gen_and_vec(vece, t, t, ones);
+ tcg_gen_shri_vec(vece, d, a, shift);
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_shri_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_urshr8_i64,
+ .fniv = gen_urshr_vec,
+ .fno = gen_helper_gvec_urshr_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni8 = gen_urshr16_i64,
+ .fniv = gen_urshr_vec,
+ .fno = gen_helper_gvec_urshr_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_urshr32_i32,
+ .fniv = gen_urshr_vec,
+ .fno = gen_helper_gvec_urshr_s,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_urshr64_i64,
+ .fniv = gen_urshr_vec,
+ .fno = gen_helper_gvec_urshr_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize] */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ if (shift == (8 << vece)) {
+ /*
+ * Shifts larger than the element size are architecturally valid.
+ * Unsigned results in zero. With rounding, this produces a
+ * copy of the most significant bit.
+ */
+ tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz);
+ } else {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ }
+}
+
+static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ if (sh == 8) {
+ tcg_gen_vec_shr8i_i64(t, a, 7);
+ } else {
+ gen_urshr8_i64(t, a, sh);
+ }
+ tcg_gen_vec_add8_i64(d, d, t);
+}
+
+static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ if (sh == 16) {
+ tcg_gen_vec_shr16i_i64(t, a, 15);
+ } else {
+ gen_urshr16_i64(t, a, sh);
+ }
+ tcg_gen_vec_add16_i64(d, d, t);
+}
+
+static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+
+ if (sh == 32) {
+ tcg_gen_shri_i32(t, a, 31);
+ } else {
+ gen_urshr32_i32(t, a, sh);
+ }
+ tcg_gen_add_i32(d, d, t);
+}
+
+static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ if (sh == 64) {
+ tcg_gen_shri_i64(t, a, 63);
+ } else {
+ gen_urshr64_i64(t, a, sh);
+ }
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+
+ if (sh == (8 << vece)) {
+ tcg_gen_shri_vec(vece, t, a, sh - 1);
+ } else {
+ gen_urshr_vec(vece, t, a, sh);
+ }
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_shri_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen2i ops[4] = {
+ { .fni8 = gen_ursra8_i64,
+ .fniv = gen_ursra_vec,
+ .fno = gen_helper_gvec_ursra_b,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_8 },
+ { .fni8 = gen_ursra16_i64,
+ .fniv = gen_ursra_vec,
+ .fno = gen_helper_gvec_ursra_h,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_16 },
+ { .fni4 = gen_ursra32_i32,
+ .fniv = gen_ursra_vec,
+ .fno = gen_helper_gvec_ursra_s,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_32 },
+ { .fni8 = gen_ursra64_i64,
+ .fniv = gen_ursra_vec,
+ .fno = gen_helper_gvec_ursra_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize] */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+}
+
+static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ uint64_t mask = dup_const(MO_8, 0xff >> shift);
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, shift);
+ tcg_gen_andi_i64(t, t, mask);
+ tcg_gen_andi_i64(d, d, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ uint64_t mask = dup_const(MO_16, 0xffff >> shift);
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, a, shift);
+ tcg_gen_andi_i64(t, t, mask);
+ tcg_gen_andi_i64(d, d, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
+{
+ tcg_gen_shri_i32(a, a, shift);
+ tcg_gen_deposit_i32(d, d, a, 0, 32 - shift);
+}
+
+static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_shri_i64(a, a, shift);
+ tcg_gen_deposit_i64(d, d, a, 0, 64 - shift);
+}
+
+static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ TCGv_vec m = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, sh));
+ tcg_gen_shri_vec(vece, t, a, sh);
+ tcg_gen_and_vec(vece, d, d, m);
+ tcg_gen_or_vec(vece, d, d, t);
+}
+
+void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 };
+ const GVecGen2i ops[4] = {
+ { .fni8 = gen_shr8_ins_i64,
+ .fniv = gen_shr_ins_vec,
+ .fno = gen_helper_gvec_sri_b,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni8 = gen_shr16_ins_i64,
+ .fniv = gen_shr_ins_vec,
+ .fno = gen_helper_gvec_sri_h,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_shr32_ins_i32,
+ .fniv = gen_shr_ins_vec,
+ .fno = gen_helper_gvec_sri_s,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_shr64_ins_i64,
+ .fniv = gen_shr_ins_vec,
+ .fno = gen_helper_gvec_sri_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [1..esize]. */
+ tcg_debug_assert(shift > 0);
+ tcg_debug_assert(shift <= (8 << vece));
+
+ /* Shift of esize leaves destination unchanged. */
+ if (shift < (8 << vece)) {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ } else {
+ /* Nop, but we do need to clear the tail. */
+ tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
+ }
+}
+
+static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ uint64_t mask = dup_const(MO_8, 0xff << shift);
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shli_i64(t, a, shift);
+ tcg_gen_andi_i64(t, t, mask);
+ tcg_gen_andi_i64(d, d, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ uint64_t mask = dup_const(MO_16, 0xffff << shift);
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shli_i64(t, a, shift);
+ tcg_gen_andi_i64(t, t, mask);
+ tcg_gen_andi_i64(d, d, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
+{
+ tcg_gen_deposit_i32(d, d, a, shift, 32 - shift);
+}
+
+static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
+{
+ tcg_gen_deposit_i64(d, d, a, shift, 64 - shift);
+}
+
+static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ TCGv_vec m = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_shli_vec(vece, t, a, sh);
+ tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh));
+ tcg_gen_and_vec(vece, d, d, m);
+ tcg_gen_or_vec(vece, d, d, t);
+}
+
+void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
+ int64_t shift, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 };
+ const GVecGen2i ops[4] = {
+ { .fni8 = gen_shl8_ins_i64,
+ .fniv = gen_shl_ins_vec,
+ .fno = gen_helper_gvec_sli_b,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni8 = gen_shl16_ins_i64,
+ .fniv = gen_shl_ins_vec,
+ .fno = gen_helper_gvec_sli_h,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_shl32_ins_i32,
+ .fniv = gen_shl_ins_vec,
+ .fno = gen_helper_gvec_sli_s,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_shl64_ins_i64,
+ .fniv = gen_shl_ins_vec,
+ .fno = gen_helper_gvec_sli_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+
+ /* tszimm encoding produces immediates in the range [0..esize-1]. */
+ tcg_debug_assert(shift >= 0);
+ tcg_debug_assert(shift < (8 << vece));
+
+ if (shift == 0) {
+ tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz);
+ } else {
+ tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
+ }
+}
+
+static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ gen_helper_neon_mul_u8(a, a, b);
+ gen_helper_neon_add_u8(d, d, a);
+}
+
+static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ gen_helper_neon_mul_u8(a, a, b);
+ gen_helper_neon_sub_u8(d, d, a);
+}
+
+static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ gen_helper_neon_mul_u16(a, a, b);
+ gen_helper_neon_add_u16(d, d, a);
+}
+
+static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ gen_helper_neon_mul_u16(a, a, b);
+ gen_helper_neon_sub_u16(d, d, a);
+}
+
+static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ tcg_gen_mul_i32(a, a, b);
+ tcg_gen_add_i32(d, d, a);
+}
+
+static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ tcg_gen_mul_i32(a, a, b);
+ tcg_gen_sub_i32(d, d, a);
+}
+
+static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ tcg_gen_mul_i64(a, a, b);
+ tcg_gen_add_i64(d, d, a);
+}
+
+static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ tcg_gen_mul_i64(a, a, b);
+ tcg_gen_sub_i64(d, d, a);
+}
+
+static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ tcg_gen_mul_vec(vece, a, a, b);
+ tcg_gen_add_vec(vece, d, d, a);
+}
+
+static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ tcg_gen_mul_vec(vece, a, a, b);
+ tcg_gen_sub_vec(vece, d, d, a);
+}
+
+/* Note that while NEON does not support VMLA and VMLS as 64-bit ops,
+ * these tables are shared with AArch64 which does support them.
+ */
+void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_mul_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fni4 = gen_mla8_i32,
+ .fniv = gen_mla_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni4 = gen_mla16_i32,
+ .fniv = gen_mla_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_mla32_i32,
+ .fniv = gen_mla_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_mla64_i64,
+ .fniv = gen_mla_vec,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_mul_vec, INDEX_op_sub_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fni4 = gen_mls8_i32,
+ .fniv = gen_mls_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni4 = gen_mls16_i32,
+ .fniv = gen_mls_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_mls32_i32,
+ .fniv = gen_mls_vec,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_mls64_i64,
+ .fniv = gen_mls_vec,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .load_dest = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+/* CMTST : test is "if (X & Y != 0)". */
+static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ tcg_gen_and_i32(d, a, b);
+ tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0));
+}
+
+void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ tcg_gen_and_i64(d, a, b);
+ tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0));
+}
+
+static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ tcg_gen_and_vec(vece, d, a, b);
+ tcg_gen_dupi_vec(vece, a, 0);
+ tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a);
+}
+
+void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 };
+ static const GVecGen3 ops[4] = {
+ { .fni4 = gen_helper_neon_tst_u8,
+ .fniv = gen_cmtst_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fni4 = gen_helper_neon_tst_u16,
+ .fniv = gen_cmtst_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_cmtst_i32,
+ .fniv = gen_cmtst_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_cmtst_i64,
+ .fniv = gen_cmtst_vec,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift)
+{
+ TCGv_i32 lval = tcg_temp_new_i32();
+ TCGv_i32 rval = tcg_temp_new_i32();
+ TCGv_i32 lsh = tcg_temp_new_i32();
+ TCGv_i32 rsh = tcg_temp_new_i32();
+ TCGv_i32 zero = tcg_constant_i32(0);
+ TCGv_i32 max = tcg_constant_i32(32);
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_ext8s_i32(lsh, shift);
+ tcg_gen_neg_i32(rsh, lsh);
+ tcg_gen_shl_i32(lval, src, lsh);
+ tcg_gen_shr_i32(rval, src, rsh);
+ tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero);
+ tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst);
+}
+
+void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift)
+{
+ TCGv_i64 lval = tcg_temp_new_i64();
+ TCGv_i64 rval = tcg_temp_new_i64();
+ TCGv_i64 lsh = tcg_temp_new_i64();
+ TCGv_i64 rsh = tcg_temp_new_i64();
+ TCGv_i64 zero = tcg_constant_i64(0);
+ TCGv_i64 max = tcg_constant_i64(64);
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_ext8s_i64(lsh, shift);
+ tcg_gen_neg_i64(rsh, lsh);
+ tcg_gen_shl_i64(lval, src, lsh);
+ tcg_gen_shr_i64(rval, src, rsh);
+ tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero);
+ tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst);
+}
+
+static void gen_ushl_vec(unsigned vece, TCGv_vec dst,
+ TCGv_vec src, TCGv_vec shift)
+{
+ TCGv_vec lval = tcg_temp_new_vec_matching(dst);
+ TCGv_vec rval = tcg_temp_new_vec_matching(dst);
+ TCGv_vec lsh = tcg_temp_new_vec_matching(dst);
+ TCGv_vec rsh = tcg_temp_new_vec_matching(dst);
+ TCGv_vec msk, max;
+
+ tcg_gen_neg_vec(vece, rsh, shift);
+ if (vece == MO_8) {
+ tcg_gen_mov_vec(lsh, shift);
+ } else {
+ msk = tcg_temp_new_vec_matching(dst);
+ tcg_gen_dupi_vec(vece, msk, 0xff);
+ tcg_gen_and_vec(vece, lsh, shift, msk);
+ tcg_gen_and_vec(vece, rsh, rsh, msk);
+ }
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_shlv_vec(vece, lval, src, lsh);
+ tcg_gen_shrv_vec(vece, rval, src, rsh);
+
+ max = tcg_temp_new_vec_matching(dst);
+ tcg_gen_dupi_vec(vece, max, 8 << vece);
+
+ /*
+ * The choice of LT (signed) and GEU (unsigned) are biased toward
+ * the instructions of the x86_64 host. For MO_8, the whole byte
+ * is significant so we must use an unsigned compare; otherwise we
+ * have already masked to a byte and so a signed compare works.
+ * Other tcg hosts have a full set of comparisons and do not care.
+ */
+ if (vece == MO_8) {
+ tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max);
+ tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max);
+ tcg_gen_andc_vec(vece, lval, lval, lsh);
+ tcg_gen_andc_vec(vece, rval, rval, rsh);
+ } else {
+ tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max);
+ tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max);
+ tcg_gen_and_vec(vece, lval, lval, lsh);
+ tcg_gen_and_vec(vece, rval, rval, rsh);
+ }
+ tcg_gen_or_vec(vece, dst, lval, rval);
+}
+
+void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_neg_vec, INDEX_op_shlv_vec,
+ INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_ushl_vec,
+ .fno = gen_helper_gvec_ushl_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fniv = gen_ushl_vec,
+ .fno = gen_helper_gvec_ushl_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_ushl_i32,
+ .fniv = gen_ushl_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_ushl_i64,
+ .fniv = gen_ushl_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift)
+{
+ TCGv_i32 lval = tcg_temp_new_i32();
+ TCGv_i32 rval = tcg_temp_new_i32();
+ TCGv_i32 lsh = tcg_temp_new_i32();
+ TCGv_i32 rsh = tcg_temp_new_i32();
+ TCGv_i32 zero = tcg_constant_i32(0);
+ TCGv_i32 max = tcg_constant_i32(31);
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_ext8s_i32(lsh, shift);
+ tcg_gen_neg_i32(rsh, lsh);
+ tcg_gen_shl_i32(lval, src, lsh);
+ tcg_gen_umin_i32(rsh, rsh, max);
+ tcg_gen_sar_i32(rval, src, rsh);
+ tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero);
+ tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval);
+}
+
+void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift)
+{
+ TCGv_i64 lval = tcg_temp_new_i64();
+ TCGv_i64 rval = tcg_temp_new_i64();
+ TCGv_i64 lsh = tcg_temp_new_i64();
+ TCGv_i64 rsh = tcg_temp_new_i64();
+ TCGv_i64 zero = tcg_constant_i64(0);
+ TCGv_i64 max = tcg_constant_i64(63);
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_ext8s_i64(lsh, shift);
+ tcg_gen_neg_i64(rsh, lsh);
+ tcg_gen_shl_i64(lval, src, lsh);
+ tcg_gen_umin_i64(rsh, rsh, max);
+ tcg_gen_sar_i64(rval, src, rsh);
+ tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero);
+ tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval);
+}
+
+static void gen_sshl_vec(unsigned vece, TCGv_vec dst,
+ TCGv_vec src, TCGv_vec shift)
+{
+ TCGv_vec lval = tcg_temp_new_vec_matching(dst);
+ TCGv_vec rval = tcg_temp_new_vec_matching(dst);
+ TCGv_vec lsh = tcg_temp_new_vec_matching(dst);
+ TCGv_vec rsh = tcg_temp_new_vec_matching(dst);
+ TCGv_vec tmp = tcg_temp_new_vec_matching(dst);
+
+ /*
+ * Rely on the TCG guarantee that out of range shifts produce
+ * unspecified results, not undefined behaviour (i.e. no trap).
+ * Discard out-of-range results after the fact.
+ */
+ tcg_gen_neg_vec(vece, rsh, shift);
+ if (vece == MO_8) {
+ tcg_gen_mov_vec(lsh, shift);
+ } else {
+ tcg_gen_dupi_vec(vece, tmp, 0xff);
+ tcg_gen_and_vec(vece, lsh, shift, tmp);
+ tcg_gen_and_vec(vece, rsh, rsh, tmp);
+ }
+
+ /* Bound rsh so out of bound right shift gets -1. */
+ tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1);
+ tcg_gen_umin_vec(vece, rsh, rsh, tmp);
+ tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp);
+
+ tcg_gen_shlv_vec(vece, lval, src, lsh);
+ tcg_gen_sarv_vec(vece, rval, src, rsh);
+
+ /* Select in-bound left shift. */
+ tcg_gen_andc_vec(vece, lval, lval, tmp);
+
+ /* Select between left and right shift. */
+ if (vece == MO_8) {
+ tcg_gen_dupi_vec(vece, tmp, 0);
+ tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval);
+ } else {
+ tcg_gen_dupi_vec(vece, tmp, 0x80);
+ tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval);
+ }
+}
+
+void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec,
+ INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_sshl_vec,
+ .fno = gen_helper_gvec_sshl_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fniv = gen_sshl_vec,
+ .fno = gen_helper_gvec_sshl_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_sshl_i32,
+ .fniv = gen_sshl_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_sshl_i64,
+ .fniv = gen_sshl_vec,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
+ TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec x = tcg_temp_new_vec_matching(t);
+ tcg_gen_add_vec(vece, x, a, b);
+ tcg_gen_usadd_vec(vece, t, a, b);
+ tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
+ tcg_gen_or_vec(vece, sat, sat, x);
+}
+
+void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen4 ops[4] = {
+ { .fniv = gen_uqadd_vec,
+ .fno = gen_helper_gvec_uqadd_b,
+ .write_aofs = true,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fniv = gen_uqadd_vec,
+ .fno = gen_helper_gvec_uqadd_h,
+ .write_aofs = true,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fniv = gen_uqadd_vec,
+ .fno = gen_helper_gvec_uqadd_s,
+ .write_aofs = true,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fniv = gen_uqadd_vec,
+ .fno = gen_helper_gvec_uqadd_d,
+ .write_aofs = true,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
+ rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
+ TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec x = tcg_temp_new_vec_matching(t);
+ tcg_gen_add_vec(vece, x, a, b);
+ tcg_gen_ssadd_vec(vece, t, a, b);
+ tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
+ tcg_gen_or_vec(vece, sat, sat, x);
+}
+
+void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0
+ };
+ static const GVecGen4 ops[4] = {
+ { .fniv = gen_sqadd_vec,
+ .fno = gen_helper_gvec_sqadd_b,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_8 },
+ { .fniv = gen_sqadd_vec,
+ .fno = gen_helper_gvec_sqadd_h,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_16 },
+ { .fniv = gen_sqadd_vec,
+ .fno = gen_helper_gvec_sqadd_s,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_32 },
+ { .fniv = gen_sqadd_vec,
+ .fno = gen_helper_gvec_sqadd_d,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
+ rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
+ TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec x = tcg_temp_new_vec_matching(t);
+ tcg_gen_sub_vec(vece, x, a, b);
+ tcg_gen_ussub_vec(vece, t, a, b);
+ tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
+ tcg_gen_or_vec(vece, sat, sat, x);
+}
+
+void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0
+ };
+ static const GVecGen4 ops[4] = {
+ { .fniv = gen_uqsub_vec,
+ .fno = gen_helper_gvec_uqsub_b,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_8 },
+ { .fniv = gen_uqsub_vec,
+ .fno = gen_helper_gvec_uqsub_h,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_16 },
+ { .fniv = gen_uqsub_vec,
+ .fno = gen_helper_gvec_uqsub_s,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_32 },
+ { .fniv = gen_uqsub_vec,
+ .fno = gen_helper_gvec_uqsub_d,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
+ rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
+ TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec x = tcg_temp_new_vec_matching(t);
+ tcg_gen_sub_vec(vece, x, a, b);
+ tcg_gen_sssub_vec(vece, t, a, b);
+ tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
+ tcg_gen_or_vec(vece, sat, sat, x);
+}
+
+void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0
+ };
+ static const GVecGen4 ops[4] = {
+ { .fniv = gen_sqsub_vec,
+ .fno = gen_helper_gvec_sqsub_b,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_8 },
+ { .fniv = gen_sqsub_vec,
+ .fno = gen_helper_gvec_sqsub_h,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_16 },
+ { .fniv = gen_sqsub_vec,
+ .fno = gen_helper_gvec_sqsub_s,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_32 },
+ { .fniv = gen_sqsub_vec,
+ .fno = gen_helper_gvec_sqsub_d,
+ .opt_opc = vecop_list,
+ .write_aofs = true,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
+ rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+
+ tcg_gen_sub_i32(t, a, b);
+ tcg_gen_sub_i32(d, b, a);
+ tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t);
+}
+
+static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_sub_i64(t, a, b);
+ tcg_gen_sub_i64(d, b, a);
+ tcg_gen_movcond_i64(TCG_COND_LT, d, a, b, d, t);
+}
+
+static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_smin_vec(vece, t, a, b);
+ tcg_gen_smax_vec(vece, d, a, b);
+ tcg_gen_sub_vec(vece, d, d, t);
+}
+
+void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_sabd_vec,
+ .fno = gen_helper_gvec_sabd_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fniv = gen_sabd_vec,
+ .fno = gen_helper_gvec_sabd_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_sabd_i32,
+ .fniv = gen_sabd_vec,
+ .fno = gen_helper_gvec_sabd_s,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_sabd_i64,
+ .fniv = gen_sabd_vec,
+ .fno = gen_helper_gvec_sabd_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+
+ tcg_gen_sub_i32(t, a, b);
+ tcg_gen_sub_i32(d, b, a);
+ tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, d, t);
+}
+
+static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_sub_i64(t, a, b);
+ tcg_gen_sub_i64(d, b, a);
+ tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, d, t);
+}
+
+static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+
+ tcg_gen_umin_vec(vece, t, a, b);
+ tcg_gen_umax_vec(vece, d, a, b);
+ tcg_gen_sub_vec(vece, d, d, t);
+}
+
+void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_uabd_vec,
+ .fno = gen_helper_gvec_uabd_b,
+ .opt_opc = vecop_list,
+ .vece = MO_8 },
+ { .fniv = gen_uabd_vec,
+ .fno = gen_helper_gvec_uabd_h,
+ .opt_opc = vecop_list,
+ .vece = MO_16 },
+ { .fni4 = gen_uabd_i32,
+ .fniv = gen_uabd_vec,
+ .fno = gen_helper_gvec_uabd_s,
+ .opt_opc = vecop_list,
+ .vece = MO_32 },
+ { .fni8 = gen_uabd_i64,
+ .fniv = gen_uabd_vec,
+ .fno = gen_helper_gvec_uabd_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_saba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+ gen_sabd_i32(t, a, b);
+ tcg_gen_add_i32(d, d, t);
+}
+
+static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+ gen_sabd_i64(t, a, b);
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ gen_sabd_vec(vece, t, a, b);
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sub_vec, INDEX_op_add_vec,
+ INDEX_op_smin_vec, INDEX_op_smax_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_saba_vec,
+ .fno = gen_helper_gvec_saba_b,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_8 },
+ { .fniv = gen_saba_vec,
+ .fno = gen_helper_gvec_saba_h,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_16 },
+ { .fni4 = gen_saba_i32,
+ .fniv = gen_saba_vec,
+ .fno = gen_helper_gvec_saba_s,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_32 },
+ { .fni8 = gen_saba_i64,
+ .fniv = gen_saba_vec,
+ .fno = gen_helper_gvec_saba_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+static void gen_uaba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
+{
+ TCGv_i32 t = tcg_temp_new_i32();
+ gen_uabd_i32(t, a, b);
+ tcg_gen_add_i32(d, d, t);
+}
+
+static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+ gen_uabd_i64(t, a, b);
+ tcg_gen_add_i64(d, d, t);
+}
+
+static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
+{
+ TCGv_vec t = tcg_temp_new_vec_matching(d);
+ gen_uabd_vec(vece, t, a, b);
+ tcg_gen_add_vec(vece, d, d, t);
+}
+
+void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = {
+ INDEX_op_sub_vec, INDEX_op_add_vec,
+ INDEX_op_umin_vec, INDEX_op_umax_vec, 0
+ };
+ static const GVecGen3 ops[4] = {
+ { .fniv = gen_uaba_vec,
+ .fno = gen_helper_gvec_uaba_b,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_8 },
+ { .fniv = gen_uaba_vec,
+ .fno = gen_helper_gvec_uaba_h,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_16 },
+ { .fni4 = gen_uaba_i32,
+ .fniv = gen_uaba_vec,
+ .fno = gen_helper_gvec_uaba_s,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_32 },
+ { .fni8 = gen_uaba_i64,
+ .fniv = gen_uaba_vec,
+ .fno = gen_helper_gvec_uaba_d,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .opt_opc = vecop_list,
+ .load_dest = true,
+ .vece = MO_64 },
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
+}
+
+void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_gvec_addp_b,
+ gen_helper_gvec_addp_h,
+ gen_helper_gvec_addp_s,
+ gen_helper_gvec_addp_d,
+ };
+ tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]);
+}
+
+void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_gvec_smaxp_b,
+ gen_helper_gvec_smaxp_h,
+ gen_helper_gvec_smaxp_s,
+ };
+ tcg_debug_assert(vece <= MO_32);
+ tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]);
+}
+
+void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_gvec_sminp_b,
+ gen_helper_gvec_sminp_h,
+ gen_helper_gvec_sminp_s,
+ };
+ tcg_debug_assert(vece <= MO_32);
+ tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]);
+}
+
+void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_gvec_umaxp_b,
+ gen_helper_gvec_umaxp_h,
+ gen_helper_gvec_umaxp_s,
+ };
+ tcg_debug_assert(vece <= MO_32);
+ tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]);
+}
+
+void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static gen_helper_gvec_3 * const fns[4] = {
+ gen_helper_gvec_uminp_b,
+ gen_helper_gvec_uminp_h,
+ gen_helper_gvec_uminp_s,
+ };
+ tcg_debug_assert(vece <= MO_32);
+ tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]);
+}
diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c
new file mode 100644
index 0000000000..093b498b13
--- /dev/null
+++ b/target/arm/tcg/gengvec64.c
@@ -0,0 +1,190 @@
+/*
+ * AArch64 generic vector expansion
+ *
+ * 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.1 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 "qemu/osdep.h"
+#include "translate.h"
+#include "translate-a64.h"
+
+
+static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
+{
+ tcg_gen_rotli_i64(d, m, 1);
+ tcg_gen_xor_i64(d, d, n);
+}
+
+static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m)
+{
+ tcg_gen_rotli_vec(vece, d, m, 1);
+ tcg_gen_xor_vec(vece, d, d, n);
+}
+
+void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 };
+ static const GVecGen3 op = {
+ .fni8 = gen_rax1_i64,
+ .fniv = gen_rax1_vec,
+ .opt_opc = vecop_list,
+ .fno = gen_helper_crypto_rax1,
+ .vece = MO_64,
+ };
+ tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op);
+}
+
+static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+ uint64_t mask = dup_const(MO_8, 0xff >> sh);
+
+ tcg_gen_xor_i64(t, n, m);
+ tcg_gen_shri_i64(d, t, sh);
+ tcg_gen_shli_i64(t, t, 8 - sh);
+ tcg_gen_andi_i64(d, d, mask);
+ tcg_gen_andi_i64(t, t, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
+{
+ TCGv_i64 t = tcg_temp_new_i64();
+ uint64_t mask = dup_const(MO_16, 0xffff >> sh);
+
+ tcg_gen_xor_i64(t, n, m);
+ tcg_gen_shri_i64(d, t, sh);
+ tcg_gen_shli_i64(t, t, 16 - sh);
+ tcg_gen_andi_i64(d, d, mask);
+ tcg_gen_andi_i64(t, t, ~mask);
+ tcg_gen_or_i64(d, d, t);
+}
+
+static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh)
+{
+ tcg_gen_xor_i32(d, n, m);
+ tcg_gen_rotri_i32(d, d, sh);
+}
+
+static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
+{
+ tcg_gen_xor_i64(d, n, m);
+ tcg_gen_rotri_i64(d, d, sh);
+}
+
+static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
+ TCGv_vec m, int64_t sh)
+{
+ tcg_gen_xor_vec(vece, d, n, m);
+ tcg_gen_rotri_vec(vece, d, d, sh);
+}
+
+void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, int64_t shift,
+ uint32_t opr_sz, uint32_t max_sz)
+{
+ static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 };
+ static const GVecGen3i ops[4] = {
+ { .fni8 = gen_xar8_i64,
+ .fniv = gen_xar_vec,
+ .fno = gen_helper_sve2_xar_b,
+ .opt_opc = vecop,
+ .vece = MO_8 },
+ { .fni8 = gen_xar16_i64,
+ .fniv = gen_xar_vec,
+ .fno = gen_helper_sve2_xar_h,
+ .opt_opc = vecop,
+ .vece = MO_16 },
+ { .fni4 = gen_xar_i32,
+ .fniv = gen_xar_vec,
+ .fno = gen_helper_sve2_xar_s,
+ .opt_opc = vecop,
+ .vece = MO_32 },
+ { .fni8 = gen_xar_i64,
+ .fniv = gen_xar_vec,
+ .fno = gen_helper_gvec_xar_d,
+ .opt_opc = vecop,
+ .vece = MO_64 }
+ };
+ int esize = 8 << vece;
+
+ /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */
+ tcg_debug_assert(shift >= 0);
+ tcg_debug_assert(shift <= esize);
+ shift &= esize - 1;
+
+ if (shift == 0) {
+ /* xar with no rotate devolves to xor. */
+ tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz);
+ } else {
+ tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz,
+ shift, &ops[vece]);
+ }
+}
+
+static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k)
+{
+ tcg_gen_xor_i64(d, n, m);
+ tcg_gen_xor_i64(d, d, k);
+}
+
+static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
+ TCGv_vec m, TCGv_vec k)
+{
+ tcg_gen_xor_vec(vece, d, n, m);
+ tcg_gen_xor_vec(vece, d, d, k);
+}
+
+void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
+ uint32_t a, uint32_t oprsz, uint32_t maxsz)
+{
+ static const GVecGen4 op = {
+ .fni8 = gen_eor3_i64,
+ .fniv = gen_eor3_vec,
+ .fno = gen_helper_sve2_eor3,
+ .vece = MO_64,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ };
+ tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op);
+}
+
+static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k)
+{
+ tcg_gen_andc_i64(d, m, k);
+ tcg_gen_xor_i64(d, d, n);
+}
+
+static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
+ TCGv_vec m, TCGv_vec k)
+{
+ tcg_gen_andc_vec(vece, d, m, k);
+ tcg_gen_xor_vec(vece, d, d, n);
+}
+
+void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
+ uint32_t a, uint32_t oprsz, uint32_t maxsz)
+{
+ static const GVecGen4 op = {
+ .fni8 = gen_bcax_i64,
+ .fniv = gen_bcax_vec,
+ .fno = gen_helper_sve2_bcax,
+ .vece = MO_64,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ };
+ tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op);
+}
+
diff --git a/target/arm/tcg/helper-a64.h b/target/arm/tcg/helper-a64.h
index 0518165399..371388f61b 100644
--- a/target/arm/tcg/helper-a64.h
+++ b/target/arm/tcg/helper-a64.h
@@ -132,3 +132,15 @@ DEF_HELPER_4(cpye, void, env, i32, i32, i32)
DEF_HELPER_4(cpyfp, void, env, i32, i32, i32)
DEF_HELPER_4(cpyfm, void, env, i32, i32, i32)
DEF_HELPER_4(cpyfe, void, env, i32, i32, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c
index d1f1e02acc..23d7f73035 100644
--- a/target/arm/tcg/m_helper.c
+++ b/target/arm/tcg/m_helper.c
@@ -16,6 +16,7 @@
#include "qemu/bitops.h"
#include "qemu/log.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#ifdef CONFIG_TCG
#include "exec/cpu_ldst.h"
#include "semihosting/common-semi.h"
diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build
index 3b1a9f0fc5..508932a249 100644
--- a/target/arm/tcg/meson.build
+++ b/target/arm/tcg/meson.build
@@ -24,6 +24,7 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64)
arm_ss.add(files(
'cpu32.c',
+ 'gengvec.c',
'translate.c',
'translate-m-nocp.c',
'translate-mve.c',
@@ -42,6 +43,7 @@ arm_ss.add(files(
arm_ss.add(when: 'TARGET_AARCH64', if_true: files(
'cpu64.c',
+ 'gengvec64.c',
'translate-a64.c',
'translate-sve.c',
'translate-sme.c',
diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c
index d971b81370..037ac6dd60 100644
--- a/target/arm/tcg/mte_helper.c
+++ b/target/arm/tcg/mte_helper.c
@@ -22,6 +22,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/ram_addr.h"
#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c
index bc6c4a54e9..a0b51c8809 100644
--- a/target/arm/tcg/neon_helper.c
+++ b/target/arm/tcg/neon_helper.c
@@ -745,11 +745,6 @@ uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b)
return (a + b) ^ mask;
}
-#define NEON_FN(dest, src1, src2) dest = src1 + src2
-NEON_POP(padd_u8, neon_u8, 4)
-NEON_POP(padd_u16, neon_u16, 2)
-#undef NEON_FN
-
#define NEON_FN(dest, src1, src2) dest = src1 - src2
NEON_VOP(sub_u8, neon_u8, 4)
NEON_VOP(sub_u16, neon_u16, 2)
diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c
index 6853f58c19..dd49e67d7a 100644
--- a/target/arm/tcg/sve_helper.c
+++ b/target/arm/tcg/sve_helper.c
@@ -21,6 +21,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/helper-proto.h"
#include "tcg/tcg-gvec-desc.h"
#include "fpu/softfloat.h"
diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c
index 976094a5c8..6680b7b2f2 100644
--- a/target/arm/tcg/translate-a64.c
+++ b/target/arm/tcg/translate-a64.c
@@ -22,7 +22,6 @@
#include "translate.h"
#include "translate-a64.h"
#include "qemu/log.h"
-#include "disas/disas.h"
#include "arm_ldst.h"
#include "semihosting/semihost.h"
#include "cpregs.h"
@@ -1315,6 +1314,67 @@ bool sme_enabled_check_with_svcr(DisasContext *s, unsigned req)
}
/*
+ * Expanders for AdvSIMD translation functions.
+ */
+
+static bool do_gvec_op2_ool(DisasContext *s, arg_qrr_e *a, int data,
+ gen_helper_gvec_2 *fn)
+{
+ if (!a->q && a->esz == MO_64) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_op2_ool(s, a->q, a->rd, a->rn, data, fn);
+ }
+ return true;
+}
+
+static bool do_gvec_op3_ool(DisasContext *s, arg_qrrr_e *a, int data,
+ gen_helper_gvec_3 *fn)
+{
+ if (!a->q && a->esz == MO_64) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_op3_ool(s, a->q, a->rd, a->rn, a->rm, data, fn);
+ }
+ return true;
+}
+
+static bool do_gvec_fn3(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn)
+{
+ if (!a->q && a->esz == MO_64) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz);
+ }
+ return true;
+}
+
+static bool do_gvec_fn3_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn)
+{
+ if (a->esz == MO_64) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz);
+ }
+ return true;
+}
+
+static bool do_gvec_fn4(DisasContext *s, arg_qrrrr_e *a, GVecGen4Fn *fn)
+{
+ if (!a->q && a->esz == MO_64) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_fn4(s, a->q, a->rd, a->rn, a->rm, a->ra, fn, a->esz);
+ }
+ return true;
+}
+
+/*
* 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.
@@ -4561,6 +4621,955 @@ static bool trans_EXTR(DisasContext *s, arg_extract *a)
return true;
}
+/*
+ * Cryptographic AES, SHA, SHA512
+ */
+
+TRANS_FEAT(AESE, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aese)
+TRANS_FEAT(AESD, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aesd)
+TRANS_FEAT(AESMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesmc)
+TRANS_FEAT(AESIMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesimc)
+
+TRANS_FEAT(SHA1C, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1c)
+TRANS_FEAT(SHA1P, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1p)
+TRANS_FEAT(SHA1M, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1m)
+TRANS_FEAT(SHA1SU0, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1su0)
+
+TRANS_FEAT(SHA256H, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h)
+TRANS_FEAT(SHA256H2, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h2)
+TRANS_FEAT(SHA256SU1, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256su1)
+
+TRANS_FEAT(SHA1H, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1h)
+TRANS_FEAT(SHA1SU1, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1su1)
+TRANS_FEAT(SHA256SU0, aa64_sha256, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha256su0)
+
+TRANS_FEAT(SHA512H, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h)
+TRANS_FEAT(SHA512H2, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h2)
+TRANS_FEAT(SHA512SU1, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512su1)
+TRANS_FEAT(RAX1, aa64_sha3, do_gvec_fn3, a, gen_gvec_rax1)
+TRANS_FEAT(SM3PARTW1, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw1)
+TRANS_FEAT(SM3PARTW2, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw2)
+TRANS_FEAT(SM4EKEY, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4ekey)
+
+TRANS_FEAT(SHA512SU0, aa64_sha512, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha512su0)
+TRANS_FEAT(SM4E, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4e)
+
+TRANS_FEAT(EOR3, aa64_sha3, do_gvec_fn4, a, gen_gvec_eor3)
+TRANS_FEAT(BCAX, aa64_sha3, do_gvec_fn4, a, gen_gvec_bcax)
+
+static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a)
+{
+ if (!dc_isar_feature(aa64_sm3, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ TCGv_i32 tcg_op1 = tcg_temp_new_i32();
+ TCGv_i32 tcg_op2 = tcg_temp_new_i32();
+ TCGv_i32 tcg_op3 = tcg_temp_new_i32();
+ TCGv_i32 tcg_res = tcg_temp_new_i32();
+ unsigned vsz, dofs;
+
+ read_vec_element_i32(s, tcg_op1, a->rn, 3, MO_32);
+ read_vec_element_i32(s, tcg_op2, a->rm, 3, MO_32);
+ read_vec_element_i32(s, tcg_op3, a->ra, 3, MO_32);
+
+ tcg_gen_rotri_i32(tcg_res, tcg_op1, 20);
+ tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2);
+ tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3);
+ tcg_gen_rotri_i32(tcg_res, tcg_res, 25);
+
+ /* Clear the whole register first, then store bits [127:96]. */
+ vsz = vec_full_reg_size(s);
+ dofs = vec_full_reg_offset(s, a->rd);
+ tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0);
+ write_vec_element_i32(s, tcg_res, a->rd, 3, MO_32);
+ }
+ return true;
+}
+
+static bool do_crypto3i(DisasContext *s, arg_crypto3i *a, gen_helper_gvec_3 *fn)
+{
+ if (fp_access_check(s)) {
+ gen_gvec_op3_ool(s, true, a->rd, a->rn, a->rm, a->imm, fn);
+ }
+ return true;
+}
+TRANS_FEAT(SM3TT1A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1a)
+TRANS_FEAT(SM3TT1B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1b)
+TRANS_FEAT(SM3TT2A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2a)
+TRANS_FEAT(SM3TT2B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2b)
+
+static bool trans_XAR(DisasContext *s, arg_XAR *a)
+{
+ if (!dc_isar_feature(aa64_sha3, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_xar(MO_64, vec_full_reg_offset(s, a->rd),
+ vec_full_reg_offset(s, a->rn),
+ vec_full_reg_offset(s, a->rm), a->imm, 16,
+ vec_full_reg_size(s));
+ }
+ return true;
+}
+
+/*
+ * Advanced SIMD copy
+ */
+
+static bool decode_esz_idx(int imm, MemOp *pesz, unsigned *pidx)
+{
+ unsigned esz = ctz32(imm);
+ if (esz <= MO_64) {
+ *pesz = esz;
+ *pidx = imm >> (esz + 1);
+ return true;
+ }
+ return false;
+}
+
+static bool trans_DUP_element_s(DisasContext *s, arg_DUP_element_s *a)
+{
+ MemOp esz;
+ unsigned idx;
+
+ if (!decode_esz_idx(a->imm, &esz, &idx)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ /*
+ * This instruction just extracts the specified element and
+ * zero-extends it into the bottom of the destination register.
+ */
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ read_vec_element(s, tmp, a->rn, idx, esz);
+ write_fp_dreg(s, a->rd, tmp);
+ }
+ return true;
+}
+
+static bool trans_DUP_element_v(DisasContext *s, arg_DUP_element_v *a)
+{
+ MemOp esz;
+ unsigned idx;
+
+ if (!decode_esz_idx(a->imm, &esz, &idx)) {
+ return false;
+ }
+ if (esz == MO_64 && !a->q) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ tcg_gen_gvec_dup_mem(esz, vec_full_reg_offset(s, a->rd),
+ vec_reg_offset(s, a->rn, idx, esz),
+ a->q ? 16 : 8, vec_full_reg_size(s));
+ }
+ return true;
+}
+
+static bool trans_DUP_general(DisasContext *s, arg_DUP_general *a)
+{
+ MemOp esz;
+ unsigned idx;
+
+ if (!decode_esz_idx(a->imm, &esz, &idx)) {
+ return false;
+ }
+ if (esz == MO_64 && !a->q) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd),
+ a->q ? 16 : 8, vec_full_reg_size(s),
+ cpu_reg(s, a->rn));
+ }
+ return true;
+}
+
+static bool do_smov_umov(DisasContext *s, arg_SMOV *a, MemOp is_signed)
+{
+ MemOp esz;
+ unsigned idx;
+
+ if (!decode_esz_idx(a->imm, &esz, &idx)) {
+ return false;
+ }
+ if (is_signed) {
+ if (esz == MO_64 || (esz == MO_32 && !a->q)) {
+ return false;
+ }
+ } else {
+ if (esz == MO_64 ? !a->q : a->q) {
+ return false;
+ }
+ }
+ if (fp_access_check(s)) {
+ TCGv_i64 tcg_rd = cpu_reg(s, a->rd);
+ read_vec_element(s, tcg_rd, a->rn, idx, esz | is_signed);
+ if (is_signed && !a->q) {
+ tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
+ }
+ }
+ return true;
+}
+
+TRANS(SMOV, do_smov_umov, a, MO_SIGN)
+TRANS(UMOV, do_smov_umov, a, 0)
+
+static bool trans_INS_general(DisasContext *s, arg_INS_general *a)
+{
+ MemOp esz;
+ unsigned idx;
+
+ if (!decode_esz_idx(a->imm, &esz, &idx)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ write_vec_element(s, cpu_reg(s, a->rn), a->rd, idx, esz);
+ clear_vec_high(s, true, a->rd);
+ }
+ return true;
+}
+
+static bool trans_INS_element(DisasContext *s, arg_INS_element *a)
+{
+ MemOp esz;
+ unsigned didx, sidx;
+
+ if (!decode_esz_idx(a->di, &esz, &didx)) {
+ return false;
+ }
+ sidx = a->si >> esz;
+ if (fp_access_check(s)) {
+ TCGv_i64 tmp = tcg_temp_new_i64();
+
+ read_vec_element(s, tmp, a->rn, sidx, esz);
+ write_vec_element(s, tmp, a->rd, didx, esz);
+
+ /* INS is considered a 128-bit write for SVE. */
+ clear_vec_high(s, true, a->rd);
+ }
+ return true;
+}
+
+/*
+ * Advanced SIMD three same
+ */
+
+typedef struct FPScalar {
+ void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr);
+ void (*gen_s)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr);
+ void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr);
+} FPScalar;
+
+static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f)
+{
+ switch (a->esz) {
+ case MO_64:
+ if (fp_access_check(s)) {
+ TCGv_i64 t0 = read_fp_dreg(s, a->rn);
+ TCGv_i64 t1 = read_fp_dreg(s, a->rm);
+ f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_dreg(s, a->rd, t0);
+ }
+ break;
+ case MO_32:
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_sreg(s, a->rn);
+ TCGv_i32 t1 = read_fp_sreg(s, a->rm);
+ f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_hreg(s, a->rn);
+ TCGv_i32 t1 = read_fp_hreg(s, a->rm);
+ f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static const FPScalar f_scalar_fadd = {
+ gen_helper_vfp_addh,
+ gen_helper_vfp_adds,
+ gen_helper_vfp_addd,
+};
+TRANS(FADD_s, do_fp3_scalar, a, &f_scalar_fadd)
+
+static const FPScalar f_scalar_fsub = {
+ gen_helper_vfp_subh,
+ gen_helper_vfp_subs,
+ gen_helper_vfp_subd,
+};
+TRANS(FSUB_s, do_fp3_scalar, a, &f_scalar_fsub)
+
+static const FPScalar f_scalar_fdiv = {
+ gen_helper_vfp_divh,
+ gen_helper_vfp_divs,
+ gen_helper_vfp_divd,
+};
+TRANS(FDIV_s, do_fp3_scalar, a, &f_scalar_fdiv)
+
+static const FPScalar f_scalar_fmul = {
+ gen_helper_vfp_mulh,
+ gen_helper_vfp_muls,
+ gen_helper_vfp_muld,
+};
+TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul)
+
+static const FPScalar f_scalar_fmax = {
+ gen_helper_advsimd_maxh,
+ gen_helper_vfp_maxs,
+ gen_helper_vfp_maxd,
+};
+TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax)
+
+static const FPScalar f_scalar_fmin = {
+ gen_helper_advsimd_minh,
+ gen_helper_vfp_mins,
+ gen_helper_vfp_mind,
+};
+TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin)
+
+static const FPScalar f_scalar_fmaxnm = {
+ gen_helper_advsimd_maxnumh,
+ gen_helper_vfp_maxnums,
+ gen_helper_vfp_maxnumd,
+};
+TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm)
+
+static const FPScalar f_scalar_fminnm = {
+ gen_helper_advsimd_minnumh,
+ gen_helper_vfp_minnums,
+ gen_helper_vfp_minnumd,
+};
+TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm)
+
+static const FPScalar f_scalar_fmulx = {
+ gen_helper_advsimd_mulxh,
+ gen_helper_vfp_mulxs,
+ gen_helper_vfp_mulxd,
+};
+TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx)
+
+static void gen_fnmul_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s)
+{
+ gen_helper_vfp_mulh(d, n, m, s);
+ gen_vfp_negh(d, d);
+}
+
+static void gen_fnmul_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s)
+{
+ gen_helper_vfp_muls(d, n, m, s);
+ gen_vfp_negs(d, d);
+}
+
+static void gen_fnmul_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s)
+{
+ gen_helper_vfp_muld(d, n, m, s);
+ gen_vfp_negd(d, d);
+}
+
+static const FPScalar f_scalar_fnmul = {
+ gen_fnmul_h,
+ gen_fnmul_s,
+ gen_fnmul_d,
+};
+TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul)
+
+static const FPScalar f_scalar_fcmeq = {
+ gen_helper_advsimd_ceq_f16,
+ gen_helper_neon_ceq_f32,
+ gen_helper_neon_ceq_f64,
+};
+TRANS(FCMEQ_s, do_fp3_scalar, a, &f_scalar_fcmeq)
+
+static const FPScalar f_scalar_fcmge = {
+ gen_helper_advsimd_cge_f16,
+ gen_helper_neon_cge_f32,
+ gen_helper_neon_cge_f64,
+};
+TRANS(FCMGE_s, do_fp3_scalar, a, &f_scalar_fcmge)
+
+static const FPScalar f_scalar_fcmgt = {
+ gen_helper_advsimd_cgt_f16,
+ gen_helper_neon_cgt_f32,
+ gen_helper_neon_cgt_f64,
+};
+TRANS(FCMGT_s, do_fp3_scalar, a, &f_scalar_fcmgt)
+
+static const FPScalar f_scalar_facge = {
+ gen_helper_advsimd_acge_f16,
+ gen_helper_neon_acge_f32,
+ gen_helper_neon_acge_f64,
+};
+TRANS(FACGE_s, do_fp3_scalar, a, &f_scalar_facge)
+
+static const FPScalar f_scalar_facgt = {
+ gen_helper_advsimd_acgt_f16,
+ gen_helper_neon_acgt_f32,
+ gen_helper_neon_acgt_f64,
+};
+TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt)
+
+static void gen_fabd_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s)
+{
+ gen_helper_vfp_subh(d, n, m, s);
+ gen_vfp_absh(d, d);
+}
+
+static void gen_fabd_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s)
+{
+ gen_helper_vfp_subs(d, n, m, s);
+ gen_vfp_abss(d, d);
+}
+
+static void gen_fabd_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s)
+{
+ gen_helper_vfp_subd(d, n, m, s);
+ gen_vfp_absd(d, d);
+}
+
+static const FPScalar f_scalar_fabd = {
+ gen_fabd_h,
+ gen_fabd_s,
+ gen_fabd_d,
+};
+TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd)
+
+static const FPScalar f_scalar_frecps = {
+ gen_helper_recpsf_f16,
+ gen_helper_recpsf_f32,
+ gen_helper_recpsf_f64,
+};
+TRANS(FRECPS_s, do_fp3_scalar, a, &f_scalar_frecps)
+
+static const FPScalar f_scalar_frsqrts = {
+ gen_helper_rsqrtsf_f16,
+ gen_helper_rsqrtsf_f32,
+ gen_helper_rsqrtsf_f64,
+};
+TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts)
+
+static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a,
+ gen_helper_gvec_3_ptr * const fns[3])
+{
+ MemOp esz = a->esz;
+
+ switch (esz) {
+ case MO_64:
+ if (!a->q) {
+ return false;
+ }
+ break;
+ case MO_32:
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm,
+ esz == MO_16, 0, fns[esz - 1]);
+ }
+ return true;
+}
+
+static gen_helper_gvec_3_ptr * const f_vector_fadd[3] = {
+ gen_helper_gvec_fadd_h,
+ gen_helper_gvec_fadd_s,
+ gen_helper_gvec_fadd_d,
+};
+TRANS(FADD_v, do_fp3_vector, a, f_vector_fadd)
+
+static gen_helper_gvec_3_ptr * const f_vector_fsub[3] = {
+ gen_helper_gvec_fsub_h,
+ gen_helper_gvec_fsub_s,
+ gen_helper_gvec_fsub_d,
+};
+TRANS(FSUB_v, do_fp3_vector, a, f_vector_fsub)
+
+static gen_helper_gvec_3_ptr * const f_vector_fdiv[3] = {
+ gen_helper_gvec_fdiv_h,
+ gen_helper_gvec_fdiv_s,
+ gen_helper_gvec_fdiv_d,
+};
+TRANS(FDIV_v, do_fp3_vector, a, f_vector_fdiv)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmul[3] = {
+ gen_helper_gvec_fmul_h,
+ gen_helper_gvec_fmul_s,
+ gen_helper_gvec_fmul_d,
+};
+TRANS(FMUL_v, do_fp3_vector, a, f_vector_fmul)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmax[3] = {
+ gen_helper_gvec_fmax_h,
+ gen_helper_gvec_fmax_s,
+ gen_helper_gvec_fmax_d,
+};
+TRANS(FMAX_v, do_fp3_vector, a, f_vector_fmax)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmin[3] = {
+ gen_helper_gvec_fmin_h,
+ gen_helper_gvec_fmin_s,
+ gen_helper_gvec_fmin_d,
+};
+TRANS(FMIN_v, do_fp3_vector, a, f_vector_fmin)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[3] = {
+ gen_helper_gvec_fmaxnum_h,
+ gen_helper_gvec_fmaxnum_s,
+ gen_helper_gvec_fmaxnum_d,
+};
+TRANS(FMAXNM_v, do_fp3_vector, a, f_vector_fmaxnm)
+
+static gen_helper_gvec_3_ptr * const f_vector_fminnm[3] = {
+ gen_helper_gvec_fminnum_h,
+ gen_helper_gvec_fminnum_s,
+ gen_helper_gvec_fminnum_d,
+};
+TRANS(FMINNM_v, do_fp3_vector, a, f_vector_fminnm)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = {
+ gen_helper_gvec_fmulx_h,
+ gen_helper_gvec_fmulx_s,
+ gen_helper_gvec_fmulx_d,
+};
+TRANS(FMULX_v, do_fp3_vector, a, f_vector_fmulx)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmla[3] = {
+ gen_helper_gvec_vfma_h,
+ gen_helper_gvec_vfma_s,
+ gen_helper_gvec_vfma_d,
+};
+TRANS(FMLA_v, do_fp3_vector, a, f_vector_fmla)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = {
+ gen_helper_gvec_vfms_h,
+ gen_helper_gvec_vfms_s,
+ gen_helper_gvec_vfms_d,
+};
+TRANS(FMLS_v, do_fp3_vector, a, f_vector_fmls)
+
+static gen_helper_gvec_3_ptr * const f_vector_fcmeq[3] = {
+ gen_helper_gvec_fceq_h,
+ gen_helper_gvec_fceq_s,
+ gen_helper_gvec_fceq_d,
+};
+TRANS(FCMEQ_v, do_fp3_vector, a, f_vector_fcmeq)
+
+static gen_helper_gvec_3_ptr * const f_vector_fcmge[3] = {
+ gen_helper_gvec_fcge_h,
+ gen_helper_gvec_fcge_s,
+ gen_helper_gvec_fcge_d,
+};
+TRANS(FCMGE_v, do_fp3_vector, a, f_vector_fcmge)
+
+static gen_helper_gvec_3_ptr * const f_vector_fcmgt[3] = {
+ gen_helper_gvec_fcgt_h,
+ gen_helper_gvec_fcgt_s,
+ gen_helper_gvec_fcgt_d,
+};
+TRANS(FCMGT_v, do_fp3_vector, a, f_vector_fcmgt)
+
+static gen_helper_gvec_3_ptr * const f_vector_facge[3] = {
+ gen_helper_gvec_facge_h,
+ gen_helper_gvec_facge_s,
+ gen_helper_gvec_facge_d,
+};
+TRANS(FACGE_v, do_fp3_vector, a, f_vector_facge)
+
+static gen_helper_gvec_3_ptr * const f_vector_facgt[3] = {
+ gen_helper_gvec_facgt_h,
+ gen_helper_gvec_facgt_s,
+ gen_helper_gvec_facgt_d,
+};
+TRANS(FACGT_v, do_fp3_vector, a, f_vector_facgt)
+
+static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = {
+ gen_helper_gvec_fabd_h,
+ gen_helper_gvec_fabd_s,
+ gen_helper_gvec_fabd_d,
+};
+TRANS(FABD_v, do_fp3_vector, a, f_vector_fabd)
+
+static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = {
+ gen_helper_gvec_recps_h,
+ gen_helper_gvec_recps_s,
+ gen_helper_gvec_recps_d,
+};
+TRANS(FRECPS_v, do_fp3_vector, a, f_vector_frecps)
+
+static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = {
+ gen_helper_gvec_rsqrts_h,
+ gen_helper_gvec_rsqrts_s,
+ gen_helper_gvec_rsqrts_d,
+};
+TRANS(FRSQRTS_v, do_fp3_vector, a, f_vector_frsqrts)
+
+static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = {
+ gen_helper_gvec_faddp_h,
+ gen_helper_gvec_faddp_s,
+ gen_helper_gvec_faddp_d,
+};
+TRANS(FADDP_v, do_fp3_vector, a, f_vector_faddp)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmaxp[3] = {
+ gen_helper_gvec_fmaxp_h,
+ gen_helper_gvec_fmaxp_s,
+ gen_helper_gvec_fmaxp_d,
+};
+TRANS(FMAXP_v, do_fp3_vector, a, f_vector_fmaxp)
+
+static gen_helper_gvec_3_ptr * const f_vector_fminp[3] = {
+ gen_helper_gvec_fminp_h,
+ gen_helper_gvec_fminp_s,
+ gen_helper_gvec_fminp_d,
+};
+TRANS(FMINP_v, do_fp3_vector, a, f_vector_fminp)
+
+static gen_helper_gvec_3_ptr * const f_vector_fmaxnmp[3] = {
+ gen_helper_gvec_fmaxnump_h,
+ gen_helper_gvec_fmaxnump_s,
+ gen_helper_gvec_fmaxnump_d,
+};
+TRANS(FMAXNMP_v, do_fp3_vector, a, f_vector_fmaxnmp)
+
+static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = {
+ gen_helper_gvec_fminnump_h,
+ gen_helper_gvec_fminnump_s,
+ gen_helper_gvec_fminnump_d,
+};
+TRANS(FMINNMP_v, do_fp3_vector, a, f_vector_fminnmp)
+
+static bool do_fmlal(DisasContext *s, arg_qrrr_e *a, bool is_s, bool is_2)
+{
+ if (fp_access_check(s)) {
+ int data = (is_2 << 1) | is_s;
+ tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd),
+ vec_full_reg_offset(s, a->rn),
+ vec_full_reg_offset(s, a->rm), tcg_env,
+ a->q ? 16 : 8, vec_full_reg_size(s),
+ data, gen_helper_gvec_fmlal_a64);
+ }
+ return true;
+}
+
+TRANS_FEAT(FMLAL_v, aa64_fhm, do_fmlal, a, false, false)
+TRANS_FEAT(FMLSL_v, aa64_fhm, do_fmlal, a, true, false)
+TRANS_FEAT(FMLAL2_v, aa64_fhm, do_fmlal, a, false, true)
+TRANS_FEAT(FMLSL2_v, aa64_fhm, do_fmlal, a, true, true)
+
+TRANS(ADDP_v, do_gvec_fn3, a, gen_gvec_addp)
+TRANS(SMAXP_v, do_gvec_fn3_no64, a, gen_gvec_smaxp)
+TRANS(SMINP_v, do_gvec_fn3_no64, a, gen_gvec_sminp)
+TRANS(UMAXP_v, do_gvec_fn3_no64, a, gen_gvec_umaxp)
+TRANS(UMINP_v, do_gvec_fn3_no64, a, gen_gvec_uminp)
+
+TRANS(AND_v, do_gvec_fn3, a, tcg_gen_gvec_and)
+TRANS(BIC_v, do_gvec_fn3, a, tcg_gen_gvec_andc)
+TRANS(ORR_v, do_gvec_fn3, a, tcg_gen_gvec_or)
+TRANS(ORN_v, do_gvec_fn3, a, tcg_gen_gvec_orc)
+TRANS(EOR_v, do_gvec_fn3, a, tcg_gen_gvec_xor)
+
+static bool do_bitsel(DisasContext *s, bool is_q, int d, int a, int b, int c)
+{
+ if (fp_access_check(s)) {
+ gen_gvec_fn4(s, is_q, d, a, b, c, tcg_gen_gvec_bitsel, 0);
+ }
+ return true;
+}
+
+TRANS(BSL_v, do_bitsel, a->q, a->rd, a->rd, a->rn, a->rm)
+TRANS(BIT_v, do_bitsel, a->q, a->rd, a->rm, a->rn, a->rd)
+TRANS(BIF_v, do_bitsel, a->q, a->rd, a->rm, a->rd, a->rn)
+
+/*
+ * Advanced SIMD scalar/vector x indexed element
+ */
+
+static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f)
+{
+ switch (a->esz) {
+ case MO_64:
+ if (fp_access_check(s)) {
+ TCGv_i64 t0 = read_fp_dreg(s, a->rn);
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ read_vec_element(s, t1, a->rm, a->idx, MO_64);
+ f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_dreg(s, a->rd, t0);
+ }
+ break;
+ case MO_32:
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_sreg(s, a->rn);
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t1, a->rm, a->idx, MO_32);
+ f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_hreg(s, a->rn);
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t1, a->rm, a->idx, MO_16);
+ f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return true;
+}
+
+TRANS(FMUL_si, do_fp3_scalar_idx, a, &f_scalar_fmul)
+TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx)
+
+static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg)
+{
+ switch (a->esz) {
+ case MO_64:
+ if (fp_access_check(s)) {
+ TCGv_i64 t0 = read_fp_dreg(s, a->rd);
+ TCGv_i64 t1 = read_fp_dreg(s, a->rn);
+ TCGv_i64 t2 = tcg_temp_new_i64();
+
+ read_vec_element(s, t2, a->rm, a->idx, MO_64);
+ if (neg) {
+ gen_vfp_negd(t1, t1);
+ }
+ gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR));
+ write_fp_dreg(s, a->rd, t0);
+ }
+ break;
+ case MO_32:
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_sreg(s, a->rd);
+ TCGv_i32 t1 = read_fp_sreg(s, a->rn);
+ TCGv_i32 t2 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t2, a->rm, a->idx, MO_32);
+ if (neg) {
+ gen_vfp_negs(t1, t1);
+ }
+ gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = read_fp_hreg(s, a->rd);
+ TCGv_i32 t1 = read_fp_hreg(s, a->rn);
+ TCGv_i32 t2 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t2, a->rm, a->idx, MO_16);
+ if (neg) {
+ gen_vfp_negh(t1, t1);
+ }
+ gen_helper_advsimd_muladdh(t0, t1, t2, t0,
+ fpstatus_ptr(FPST_FPCR_F16));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return true;
+}
+
+TRANS(FMLA_si, do_fmla_scalar_idx, a, false)
+TRANS(FMLS_si, do_fmla_scalar_idx, a, true)
+
+static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a,
+ gen_helper_gvec_3_ptr * const fns[3])
+{
+ MemOp esz = a->esz;
+
+ switch (esz) {
+ case MO_64:
+ if (!a->q) {
+ return false;
+ }
+ break;
+ case MO_32:
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm,
+ esz == MO_16, a->idx, fns[esz - 1]);
+ }
+ return true;
+}
+
+static gen_helper_gvec_3_ptr * const f_vector_idx_fmul[3] = {
+ gen_helper_gvec_fmul_idx_h,
+ gen_helper_gvec_fmul_idx_s,
+ gen_helper_gvec_fmul_idx_d,
+};
+TRANS(FMUL_vi, do_fp3_vector_idx, a, f_vector_idx_fmul)
+
+static gen_helper_gvec_3_ptr * const f_vector_idx_fmulx[3] = {
+ gen_helper_gvec_fmulx_idx_h,
+ gen_helper_gvec_fmulx_idx_s,
+ gen_helper_gvec_fmulx_idx_d,
+};
+TRANS(FMULX_vi, do_fp3_vector_idx, a, f_vector_idx_fmulx)
+
+static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg)
+{
+ static gen_helper_gvec_4_ptr * const fns[3] = {
+ gen_helper_gvec_fmla_idx_h,
+ gen_helper_gvec_fmla_idx_s,
+ gen_helper_gvec_fmla_idx_d,
+ };
+ MemOp esz = a->esz;
+
+ switch (esz) {
+ case MO_64:
+ if (!a->q) {
+ return false;
+ }
+ break;
+ case MO_32:
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (fp_access_check(s)) {
+ gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd,
+ esz == MO_16, (a->idx << 1) | neg,
+ fns[esz - 1]);
+ }
+ return true;
+}
+
+TRANS(FMLA_vi, do_fmla_vector_idx, a, false)
+TRANS(FMLS_vi, do_fmla_vector_idx, a, true)
+
+static bool do_fmlal_idx(DisasContext *s, arg_qrrx_e *a, bool is_s, bool is_2)
+{
+ if (fp_access_check(s)) {
+ int data = (a->idx << 2) | (is_2 << 1) | is_s;
+ tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd),
+ vec_full_reg_offset(s, a->rn),
+ vec_full_reg_offset(s, a->rm), tcg_env,
+ a->q ? 16 : 8, vec_full_reg_size(s),
+ data, gen_helper_gvec_fmlal_idx_a64);
+ }
+ return true;
+}
+
+TRANS_FEAT(FMLAL_vi, aa64_fhm, do_fmlal_idx, a, false, false)
+TRANS_FEAT(FMLSL_vi, aa64_fhm, do_fmlal_idx, a, true, false)
+TRANS_FEAT(FMLAL2_vi, aa64_fhm, do_fmlal_idx, a, false, true)
+TRANS_FEAT(FMLSL2_vi, aa64_fhm, do_fmlal_idx, a, true, true)
+
+/*
+ * Advanced SIMD scalar pairwise
+ */
+
+static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f)
+{
+ switch (a->esz) {
+ case MO_64:
+ if (fp_access_check(s)) {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ read_vec_element(s, t0, a->rn, 0, MO_64);
+ read_vec_element(s, t1, a->rn, 1, MO_64);
+ f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_dreg(s, a->rd, t0);
+ }
+ break;
+ case MO_32:
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t0, a->rn, 0, MO_32);
+ read_vec_element_i32(s, t1, a->rn, 1, MO_32);
+ f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ case MO_16:
+ if (!dc_isar_feature(aa64_fp16, s)) {
+ return false;
+ }
+ if (fp_access_check(s)) {
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ read_vec_element_i32(s, t0, a->rn, 0, MO_16);
+ read_vec_element_i32(s, t1, a->rn, 1, MO_16);
+ f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16));
+ write_fp_sreg(s, a->rd, t0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return true;
+}
+
+TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd)
+TRANS(FMAXP_s, do_fp3_scalar_pair, a, &f_scalar_fmax)
+TRANS(FMINP_s, do_fp3_scalar_pair, a, &f_scalar_fmin)
+TRANS(FMAXNMP_s, do_fp3_scalar_pair, a, &f_scalar_fmaxnm)
+TRANS(FMINNMP_s, do_fp3_scalar_pair, a, &f_scalar_fminnm)
+
+static bool trans_ADDP_s(DisasContext *s, arg_rr_e *a)
+{
+ if (fp_access_check(s)) {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ read_vec_element(s, t0, a->rn, 0, MO_64);
+ read_vec_element(s, t1, a->rn, 1, MO_64);
+ tcg_gen_add_i64(t0, t0, t1);
+ write_fp_dreg(s, a->rd, t0);
+ }
+ return true;
+}
+
/* 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
@@ -6019,10 +7028,10 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn)
tcg_gen_mov_i32(tcg_res, tcg_op);
break;
case 0x1: /* FABS */
- tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff);
+ gen_vfp_absh(tcg_res, tcg_op);
break;
case 0x2: /* FNEG */
- tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000);
+ gen_vfp_negh(tcg_res, tcg_op);
break;
case 0x3: /* FSQRT */
fpst = fpstatus_ptr(FPST_FPCR_F16);
@@ -6073,10 +7082,10 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
tcg_gen_mov_i32(tcg_res, tcg_op);
goto done;
case 0x1: /* FABS */
- gen_helper_vfp_abss(tcg_res, tcg_op);
+ gen_vfp_abss(tcg_res, tcg_op);
goto done;
case 0x2: /* FNEG */
- gen_helper_vfp_negs(tcg_res, tcg_op);
+ gen_vfp_negs(tcg_res, tcg_op);
goto done;
case 0x3: /* FSQRT */
gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env);
@@ -6148,10 +7157,10 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
switch (opcode) {
case 0x1: /* FABS */
- gen_helper_vfp_absd(tcg_res, tcg_op);
+ gen_vfp_absd(tcg_res, tcg_op);
goto done;
case 0x2: /* FNEG */
- gen_helper_vfp_negd(tcg_res, tcg_op);
+ gen_vfp_negd(tcg_res, tcg_op);
goto done;
case 0x3: /* FSQRT */
gen_helper_vfp_sqrtd(tcg_res, tcg_op, tcg_env);
@@ -6360,200 +7369,6 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn)
}
}
-/* 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 = fpstatus_ptr(FPST_FPCR);
- 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);
-}
-
-/* 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 = fpstatus_ptr(FPST_FPCR);
- 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);
-}
-
-/* Floating-point data-processing (2 source) - half precision */
-static void handle_fp_2src_half(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 = fpstatus_ptr(FPST_FPCR_F16);
- tcg_op1 = read_fp_hreg(s, rn);
- tcg_op2 = read_fp_hreg(s, rm);
-
- switch (opcode) {
- case 0x0: /* FMUL */
- gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1: /* FDIV */
- gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2: /* FADD */
- gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3: /* FSUB */
- gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x4: /* FMAX */
- gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5: /* FMIN */
- gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x6: /* FMAXNM */
- gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7: /* FMINNM */
- gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x8: /* FNMUL */
- gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst);
- tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_fp_sreg(s, rd, tcg_res);
-}
-
-/* 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 mos = extract32(insn, 29, 3);
- 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 || mos) {
- unallocated_encoding(s);
- return;
- }
-
- switch (type) {
- case 0:
- if (!fp_access_check(s)) {
- return;
- }
- handle_fp_2src_single(s, opcode, rd, rn, rm);
- break;
- case 1:
- if (!fp_access_check(s)) {
- return;
- }
- handle_fp_2src_double(s, opcode, rd, rn, rm);
- break;
- case 3:
- if (!dc_isar_feature(aa64_fp16, s)) {
- unallocated_encoding(s);
- return;
- }
- if (!fp_access_check(s)) {
- return;
- }
- handle_fp_2src_half(s, opcode, rd, rn, rm);
- break;
- default:
- unallocated_encoding(s);
- }
-}
-
/* 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)
@@ -6574,11 +7389,11 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1,
* flipped if it is a negated-input.
*/
if (o1 == true) {
- gen_helper_vfp_negs(tcg_op3, tcg_op3);
+ gen_vfp_negs(tcg_op3, tcg_op3);
}
if (o0 != o1) {
- gen_helper_vfp_negs(tcg_op1, tcg_op1);
+ gen_vfp_negs(tcg_op1, tcg_op1);
}
gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst);
@@ -6606,11 +7421,11 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
* flipped if it is a negated-input.
*/
if (o1 == true) {
- gen_helper_vfp_negd(tcg_op3, tcg_op3);
+ gen_vfp_negd(tcg_op3, tcg_op3);
}
if (o0 != o1) {
- gen_helper_vfp_negd(tcg_op1, tcg_op1);
+ gen_vfp_negd(tcg_op1, tcg_op1);
}
gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst);
@@ -7157,7 +7972,7 @@ static void disas_data_proc_fp(DisasContext *s, uint32_t insn)
break;
case 2:
/* Floating point data-processing (2 source) */
- disas_fp_2src(s, insn);
+ unallocated_encoding(s); /* in decodetree */
break;
case 3:
/* Floating point conditional select */
@@ -7619,268 +8434,6 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn)
write_fp_dreg(s, rd, tcg_res);
}
-/* DUP (Element, Vector)
- *
- * 31 30 29 21 20 16 15 10 9 5 4 0
- * +---+---+-------------------+--------+-------------+------+------+
- * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd |
- * +---+---+-------------------+--------+-------------+------+------+
- *
- * size: encoded in imm5 (see ARM ARM LowestSetBit())
- */
-static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn,
- int imm5)
-{
- int size = ctz32(imm5);
- int index;
-
- if (size > 3 || (size == 3 && !is_q)) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- index = imm5 >> (size + 1);
- tcg_gen_gvec_dup_mem(size, vec_full_reg_offset(s, rd),
- vec_reg_offset(s, rn, index, size),
- is_q ? 16 : 8, vec_full_reg_size(s));
-}
-
-/* DUP (element, scalar)
- * 31 21 20 16 15 10 9 5 4 0
- * +-----------------------+--------+-------------+------+------+
- * | 0 1 0 1 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd |
- * +-----------------------+--------+-------------+------+------+
- */
-static void handle_simd_dupes(DisasContext *s, int rd, int rn,
- int imm5)
-{
- int size = ctz32(imm5);
- int index;
- TCGv_i64 tmp;
-
- if (size > 3) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- index = imm5 >> (size + 1);
-
- /* This instruction just extracts the specified element and
- * zero-extends it into the bottom of the destination register.
- */
- tmp = tcg_temp_new_i64();
- read_vec_element(s, tmp, rn, index, size);
- write_fp_dreg(s, rd, tmp);
-}
-
-/* DUP (General)
- *
- * 31 30 29 21 20 16 15 10 9 5 4 0
- * +---+---+-------------------+--------+-------------+------+------+
- * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 1 1 | Rn | Rd |
- * +---+---+-------------------+--------+-------------+------+------+
- *
- * size: encoded in imm5 (see ARM ARM LowestSetBit())
- */
-static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn,
- int imm5)
-{
- int size = ctz32(imm5);
- uint32_t dofs, oprsz, maxsz;
-
- if (size > 3 || ((size == 3) && !is_q)) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- dofs = vec_full_reg_offset(s, rd);
- oprsz = is_q ? 16 : 8;
- maxsz = vec_full_reg_size(s);
-
- tcg_gen_gvec_dup_i64(size, dofs, oprsz, maxsz, cpu_reg(s, rn));
-}
-
-/* INS (Element)
- *
- * 31 21 20 16 15 14 11 10 9 5 4 0
- * +-----------------------+--------+------------+---+------+------+
- * | 0 1 1 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
- * +-----------------------+--------+------------+---+------+------+
- *
- * size: encoded in imm5 (see ARM ARM LowestSetBit())
- * index: encoded in imm5<4:size+1>
- */
-static void handle_simd_inse(DisasContext *s, int rd, int rn,
- int imm4, int imm5)
-{
- int size = ctz32(imm5);
- int src_index, dst_index;
- TCGv_i64 tmp;
-
- if (size > 3) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- dst_index = extract32(imm5, 1+size, 5);
- src_index = extract32(imm4, size, 4);
-
- tmp = tcg_temp_new_i64();
-
- read_vec_element(s, tmp, rn, src_index, size);
- write_vec_element(s, tmp, rd, dst_index, size);
-
- /* INS is considered a 128-bit write for SVE. */
- clear_vec_high(s, true, rd);
-}
-
-
-/* INS (General)
- *
- * 31 21 20 16 15 10 9 5 4 0
- * +-----------------------+--------+-------------+------+------+
- * | 0 1 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 1 1 1 | Rn | Rd |
- * +-----------------------+--------+-------------+------+------+
- *
- * size: encoded in imm5 (see ARM ARM LowestSetBit())
- * index: encoded in imm5<4:size+1>
- */
-static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5)
-{
- int size = ctz32(imm5);
- int idx;
-
- if (size > 3) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- idx = extract32(imm5, 1 + size, 4 - size);
- write_vec_element(s, cpu_reg(s, rn), rd, idx, size);
-
- /* INS is considered a 128-bit write for SVE. */
- clear_vec_high(s, true, rd);
-}
-
-/*
- * UMOV (General)
- * SMOV (General)
- *
- * 31 30 29 21 20 16 15 12 10 9 5 4 0
- * +---+---+-------------------+--------+-------------+------+------+
- * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 1 U 1 1 | Rn | Rd |
- * +---+---+-------------------+--------+-------------+------+------+
- *
- * U: unsigned when set
- * size: encoded in imm5 (see ARM ARM LowestSetBit())
- */
-static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed,
- int rn, int rd, int imm5)
-{
- int size = ctz32(imm5);
- int element;
- TCGv_i64 tcg_rd;
-
- /* Check for UnallocatedEncodings */
- if (is_signed) {
- if (size > 2 || (size == 2 && !is_q)) {
- unallocated_encoding(s);
- return;
- }
- } else {
- if (size > 3
- || (size < 3 && is_q)
- || (size == 3 && !is_q)) {
- unallocated_encoding(s);
- return;
- }
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- element = extract32(imm5, 1+size, 4);
-
- tcg_rd = cpu_reg(s, rd);
- read_vec_element(s, tcg_rd, rn, element, size | (is_signed ? MO_SIGN : 0));
- if (is_signed && !is_q) {
- tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
- }
-}
-
-/* AdvSIMD copy
- * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
- * +---+---+----+-----------------+------+---+------+---+------+------+
- * | 0 | Q | op | 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
- * +---+---+----+-----------------+------+---+------+---+------+------+
- */
-static void disas_simd_copy(DisasContext *s, uint32_t insn)
-{
- int rd = extract32(insn, 0, 5);
- int rn = extract32(insn, 5, 5);
- int imm4 = extract32(insn, 11, 4);
- int op = extract32(insn, 29, 1);
- int is_q = extract32(insn, 30, 1);
- int imm5 = extract32(insn, 16, 5);
-
- if (op) {
- if (is_q) {
- /* INS (element) */
- handle_simd_inse(s, rd, rn, imm4, imm5);
- } else {
- unallocated_encoding(s);
- }
- } else {
- switch (imm4) {
- case 0:
- /* DUP (element - vector) */
- handle_simd_dupe(s, is_q, rd, rn, imm5);
- break;
- case 1:
- /* DUP (general) */
- handle_simd_dupg(s, is_q, rd, rn, imm5);
- break;
- case 3:
- if (is_q) {
- /* INS (general) */
- handle_simd_insg(s, rd, rn, imm5);
- } else {
- unallocated_encoding(s);
- }
- break;
- case 5:
- case 7:
- /* UMOV/SMOV (is_q indicates 32/64; imm4 indicates signedness) */
- handle_simd_umov_smov(s, is_q, (imm4 == 5), rn, rd, imm5);
- break;
- default:
- unallocated_encoding(s);
- break;
- }
- }
-}
-
/* AdvSIMD modified immediate
* 31 30 29 28 19 18 16 15 12 11 10 9 5 4 0
* +---+---+----+---------------------+-----+-------+----+---+-------+------+
@@ -7940,176 +8493,6 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn)
}
}
-/* AdvSIMD scalar copy
- * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
- * +-----+----+-----------------+------+---+------+---+------+------+
- * | 0 1 | op | 1 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
- * +-----+----+-----------------+------+---+------+---+------+------+
- */
-static void disas_simd_scalar_copy(DisasContext *s, uint32_t insn)
-{
- int rd = extract32(insn, 0, 5);
- int rn = extract32(insn, 5, 5);
- int imm4 = extract32(insn, 11, 4);
- int imm5 = extract32(insn, 16, 5);
- int op = extract32(insn, 29, 1);
-
- if (op != 0 || imm4 != 0) {
- unallocated_encoding(s);
- return;
- }
-
- /* DUP (element, scalar) */
- handle_simd_dupes(s, rd, rn, imm5);
-}
-
-/* AdvSIMD scalar pairwise
- * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
- * +-----+---+-----------+------+-----------+--------+-----+------+------+
- * | 0 1 | U | 1 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd |
- * +-----+---+-----------+------+-----------+--------+-----+------+------+
- */
-static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn)
-{
- int u = extract32(insn, 29, 1);
- int size = extract32(insn, 22, 2);
- int opcode = extract32(insn, 12, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- TCGv_ptr fpst;
-
- /* For some ops (the FP ones), size[1] is part of the encoding.
- * For ADDP strictly it is not but size[1] is always 1 for valid
- * encodings.
- */
- opcode |= (extract32(size, 1, 1) << 5);
-
- switch (opcode) {
- case 0x3b: /* ADDP */
- if (u || size != 3) {
- unallocated_encoding(s);
- return;
- }
- if (!fp_access_check(s)) {
- return;
- }
-
- fpst = NULL;
- break;
- case 0xc: /* FMAXNMP */
- case 0xd: /* FADDP */
- case 0xf: /* FMAXP */
- case 0x2c: /* FMINNMP */
- case 0x2f: /* FMINP */
- /* FP op, size[0] is 32 or 64 bit*/
- if (!u) {
- if (!dc_isar_feature(aa64_fp16, s)) {
- unallocated_encoding(s);
- return;
- } else {
- size = MO_16;
- }
- } else {
- size = extract32(size, 0, 1) ? MO_64 : MO_32;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR);
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (size == MO_64) {
- TCGv_i64 tcg_op1 = tcg_temp_new_i64();
- TCGv_i64 tcg_op2 = tcg_temp_new_i64();
- TCGv_i64 tcg_res = tcg_temp_new_i64();
-
- read_vec_element(s, tcg_op1, rn, 0, MO_64);
- read_vec_element(s, tcg_op2, rn, 1, MO_64);
-
- switch (opcode) {
- case 0x3b: /* ADDP */
- tcg_gen_add_i64(tcg_res, tcg_op1, tcg_op2);
- break;
- case 0xc: /* FMAXNMP */
- gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xd: /* FADDP */
- gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xf: /* FMAXP */
- gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2c: /* FMINNMP */
- gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2f: /* FMINP */
- gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_fp_dreg(s, rd, tcg_res);
- } else {
- TCGv_i32 tcg_op1 = tcg_temp_new_i32();
- TCGv_i32 tcg_op2 = tcg_temp_new_i32();
- TCGv_i32 tcg_res = tcg_temp_new_i32();
-
- read_vec_element_i32(s, tcg_op1, rn, 0, size);
- read_vec_element_i32(s, tcg_op2, rn, 1, size);
-
- if (size == MO_16) {
- switch (opcode) {
- case 0xc: /* FMAXNMP */
- gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xd: /* FADDP */
- gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xf: /* FMAXP */
- gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2c: /* FMINNMP */
- gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2f: /* FMINP */
- gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- } else {
- switch (opcode) {
- case 0xc: /* FMAXNMP */
- gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xd: /* FADDP */
- gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xf: /* FMAXP */
- gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2c: /* FMINNMP */
- gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x2f: /* FMINP */
- gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- }
-
- write_fp_sreg(s, rd, tcg_res);
- }
-}
-
/*
* Common SSHR[RA]/USHR[RA] - Shift right (optional rounding/accumulate)
*
@@ -8973,183 +9356,6 @@ static void handle_3same_64(DisasContext *s, int opcode, bool u,
}
}
-/* Handle the 3-same-operands float operations; shared by the scalar
- * and vector encodings. The caller must filter out any encodings
- * not allocated for the encoding it is dealing with.
- */
-static void handle_3same_float(DisasContext *s, int size, int elements,
- int fpopcode, int rd, int rn, int rm)
-{
- int pass;
- TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR);
-
- for (pass = 0; pass < elements; pass++) {
- if (size) {
- /* Double */
- TCGv_i64 tcg_op1 = tcg_temp_new_i64();
- TCGv_i64 tcg_op2 = tcg_temp_new_i64();
- TCGv_i64 tcg_res = tcg_temp_new_i64();
-
- read_vec_element(s, tcg_op1, rn, pass, MO_64);
- read_vec_element(s, tcg_op2, rm, pass, MO_64);
-
- switch (fpopcode) {
- case 0x39: /* FMLS */
- /* As usual for ARM, separate negation for fused multiply-add */
- gen_helper_vfp_negd(tcg_op1, tcg_op1);
- /* fall through */
- case 0x19: /* FMLA */
- read_vec_element(s, tcg_res, rd, pass, MO_64);
- gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2,
- tcg_res, fpst);
- break;
- case 0x18: /* FMAXNM */
- gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1a: /* FADD */
- gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1b: /* FMULX */
- gen_helper_vfp_mulxd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1c: /* FCMEQ */
- gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1e: /* FMAX */
- gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1f: /* FRECPS */
- gen_helper_recpsf_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x38: /* FMINNM */
- gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3a: /* FSUB */
- gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3e: /* FMIN */
- gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3f: /* FRSQRTS */
- gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5b: /* FMUL */
- gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5c: /* FCMGE */
- gen_helper_neon_cge_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5d: /* FACGE */
- gen_helper_neon_acge_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5f: /* FDIV */
- gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7a: /* FABD */
- gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst);
- gen_helper_vfp_absd(tcg_res, tcg_res);
- break;
- case 0x7c: /* FCMGT */
- gen_helper_neon_cgt_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7d: /* FACGT */
- gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_vec_element(s, tcg_res, rd, pass, MO_64);
- } else {
- /* Single */
- TCGv_i32 tcg_op1 = tcg_temp_new_i32();
- TCGv_i32 tcg_op2 = tcg_temp_new_i32();
- TCGv_i32 tcg_res = tcg_temp_new_i32();
-
- read_vec_element_i32(s, tcg_op1, rn, pass, MO_32);
- read_vec_element_i32(s, tcg_op2, rm, pass, MO_32);
-
- switch (fpopcode) {
- case 0x39: /* FMLS */
- /* As usual for ARM, separate negation for fused multiply-add */
- gen_helper_vfp_negs(tcg_op1, tcg_op1);
- /* fall through */
- case 0x19: /* FMLA */
- read_vec_element_i32(s, tcg_res, rd, pass, MO_32);
- gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2,
- tcg_res, fpst);
- break;
- case 0x1a: /* FADD */
- gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1b: /* FMULX */
- gen_helper_vfp_mulxs(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1c: /* FCMEQ */
- gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1e: /* FMAX */
- gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1f: /* FRECPS */
- gen_helper_recpsf_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x18: /* FMAXNM */
- gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x38: /* FMINNM */
- gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3a: /* FSUB */
- gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3e: /* FMIN */
- gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3f: /* FRSQRTS */
- gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5b: /* FMUL */
- gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5c: /* FCMGE */
- gen_helper_neon_cge_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5d: /* FACGE */
- gen_helper_neon_acge_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x5f: /* FDIV */
- gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7a: /* FABD */
- gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst);
- gen_helper_vfp_abss(tcg_res, tcg_res);
- break;
- case 0x7c: /* FCMGT */
- gen_helper_neon_cgt_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7d: /* FACGT */
- gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- if (elements == 1) {
- /* scalar single so clear high part */
- TCGv_i64 tcg_tmp = tcg_temp_new_i64();
-
- tcg_gen_extu_i32_i64(tcg_tmp, tcg_res);
- write_vec_element(s, tcg_tmp, rd, pass, MO_64);
- } else {
- write_vec_element_i32(s, tcg_res, rd, pass, MO_32);
- }
- }
- }
-
- clear_vec_high(s, elements * (size ? 8 : 4) > 8, rd);
-}
-
/* AdvSIMD scalar three same
* 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0
* +-----+---+-----------+------+---+------+--------+---+------+------+
@@ -9166,33 +9372,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn)
bool u = extract32(insn, 29, 1);
TCGv_i64 tcg_rd;
- if (opcode >= 0x18) {
- /* Floating point: U, size[1] and opcode indicate operation */
- int fpopcode = opcode | (extract32(size, 1, 1) << 5) | (u << 6);
- switch (fpopcode) {
- case 0x1b: /* FMULX */
- case 0x1f: /* FRECPS */
- case 0x3f: /* FRSQRTS */
- case 0x5d: /* FACGE */
- case 0x7d: /* FACGT */
- case 0x1c: /* FCMEQ */
- case 0x5c: /* FCMGE */
- case 0x7c: /* FCMGT */
- case 0x7a: /* FABD */
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm);
- return;
- }
-
switch (opcode) {
case 0x1: /* SQADD, UQADD */
case 0x5: /* SQSUB, UQSUB */
@@ -9309,95 +9488,6 @@ static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn)
write_fp_dreg(s, rd, tcg_rd);
}
-/* AdvSIMD scalar three same FP16
- * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0
- * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+
- * | 0 1 | U | 1 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd |
- * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+
- * v: 0101 1110 0100 0000 0000 0100 0000 0000 => 5e400400
- * m: 1101 1111 0110 0000 1100 0100 0000 0000 => df60c400
- */
-static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s,
- uint32_t insn)
-{
- int rd = extract32(insn, 0, 5);
- int rn = extract32(insn, 5, 5);
- int opcode = extract32(insn, 11, 3);
- int rm = extract32(insn, 16, 5);
- bool u = extract32(insn, 29, 1);
- bool a = extract32(insn, 23, 1);
- int fpopcode = opcode | (a << 3) | (u << 4);
- TCGv_ptr fpst;
- TCGv_i32 tcg_op1;
- TCGv_i32 tcg_op2;
- TCGv_i32 tcg_res;
-
- switch (fpopcode) {
- case 0x03: /* FMULX */
- case 0x04: /* FCMEQ (reg) */
- case 0x07: /* FRECPS */
- case 0x0f: /* FRSQRTS */
- case 0x14: /* FCMGE (reg) */
- case 0x15: /* FACGE */
- case 0x1a: /* FABD */
- case 0x1c: /* FCMGT (reg) */
- case 0x1d: /* FACGT */
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!dc_isar_feature(aa64_fp16, s)) {
- unallocated_encoding(s);
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- fpst = fpstatus_ptr(FPST_FPCR_F16);
-
- tcg_op1 = read_fp_hreg(s, rn);
- tcg_op2 = read_fp_hreg(s, rm);
- tcg_res = tcg_temp_new_i32();
-
- switch (fpopcode) {
- case 0x03: /* FMULX */
- gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x04: /* FCMEQ (reg) */
- gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x07: /* FRECPS */
- gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x0f: /* FRSQRTS */
- gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x14: /* FCMGE (reg) */
- gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x15: /* FACGE */
- gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1a: /* FABD */
- gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst);
- tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff);
- break;
- case 0x1c: /* FCMGT (reg) */
- gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1d: /* FACGT */
- gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_fp_sreg(s, rd, tcg_res);
-}
-
/* AdvSIMD scalar three same extra
* 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0
* +-----+---+-----------+------+---+------+---+--------+---+----+----+
@@ -9529,10 +9619,10 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u,
}
break;
case 0x2f: /* FABS */
- gen_helper_vfp_absd(tcg_rd, tcg_rn);
+ gen_vfp_absd(tcg_rd, tcg_rn);
break;
case 0x6f: /* FNEG */
- gen_helper_vfp_negd(tcg_rd, tcg_rn);
+ gen_vfp_negd(tcg_rd, tcg_rn);
break;
case 0x7f: /* FSQRT */
gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_env);
@@ -10822,284 +10912,6 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn)
}
}
-/* Logic op (opcode == 3) subgroup of C3.6.16. */
-static void disas_simd_3same_logic(DisasContext *s, uint32_t insn)
-{
- int rd = extract32(insn, 0, 5);
- int rn = extract32(insn, 5, 5);
- int rm = extract32(insn, 16, 5);
- int size = extract32(insn, 22, 2);
- bool is_u = extract32(insn, 29, 1);
- bool is_q = extract32(insn, 30, 1);
-
- if (!fp_access_check(s)) {
- return;
- }
-
- switch (size + 4 * is_u) {
- case 0: /* AND */
- gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_and, 0);
- return;
- case 1: /* BIC */
- gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_andc, 0);
- return;
- case 2: /* ORR */
- gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_or, 0);
- return;
- case 3: /* ORN */
- gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_orc, 0);
- return;
- case 4: /* EOR */
- gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_xor, 0);
- return;
-
- case 5: /* BSL bitwise select */
- gen_gvec_fn4(s, is_q, rd, rd, rn, rm, tcg_gen_gvec_bitsel, 0);
- return;
- case 6: /* BIT, bitwise insert if true */
- gen_gvec_fn4(s, is_q, rd, rm, rn, rd, tcg_gen_gvec_bitsel, 0);
- return;
- case 7: /* BIF, bitwise insert if false */
- gen_gvec_fn4(s, is_q, rd, rm, rd, rn, tcg_gen_gvec_bitsel, 0);
- return;
-
- default:
- g_assert_not_reached();
- }
-}
-
-/* Pairwise op subgroup of C3.6.16.
- *
- * This is called directly or via the handle_3same_float for float pairwise
- * operations where the opcode and size are calculated differently.
- */
-static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode,
- int size, int rn, int rm, int rd)
-{
- TCGv_ptr fpst;
- int pass;
-
- /* Floating point operations need fpst */
- if (opcode >= 0x58) {
- fpst = fpstatus_ptr(FPST_FPCR);
- } else {
- fpst = NULL;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- /* These operations work on the concatenated rm:rn, with each pair of
- * adjacent elements being operated on to produce an element in the result.
- */
- if (size == 3) {
- TCGv_i64 tcg_res[2];
-
- for (pass = 0; pass < 2; pass++) {
- TCGv_i64 tcg_op1 = tcg_temp_new_i64();
- TCGv_i64 tcg_op2 = tcg_temp_new_i64();
- int passreg = (pass == 0) ? rn : rm;
-
- read_vec_element(s, tcg_op1, passreg, 0, MO_64);
- read_vec_element(s, tcg_op2, passreg, 1, MO_64);
- tcg_res[pass] = tcg_temp_new_i64();
-
- switch (opcode) {
- case 0x17: /* ADDP */
- tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2);
- break;
- case 0x58: /* FMAXNMP */
- gen_helper_vfp_maxnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x5a: /* FADDP */
- gen_helper_vfp_addd(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x5e: /* FMAXP */
- gen_helper_vfp_maxd(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x78: /* FMINNMP */
- gen_helper_vfp_minnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x7e: /* FMINP */
- gen_helper_vfp_mind(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- }
-
- for (pass = 0; pass < 2; pass++) {
- write_vec_element(s, tcg_res[pass], rd, pass, MO_64);
- }
- } else {
- int maxpass = is_q ? 4 : 2;
- TCGv_i32 tcg_res[4];
-
- for (pass = 0; pass < maxpass; pass++) {
- TCGv_i32 tcg_op1 = tcg_temp_new_i32();
- TCGv_i32 tcg_op2 = tcg_temp_new_i32();
- NeonGenTwoOpFn *genfn = NULL;
- int passreg = pass < (maxpass / 2) ? rn : rm;
- int passelt = (is_q && (pass & 1)) ? 2 : 0;
-
- read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_32);
- read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_32);
- tcg_res[pass] = tcg_temp_new_i32();
-
- switch (opcode) {
- case 0x17: /* ADDP */
- {
- static NeonGenTwoOpFn * const fns[3] = {
- gen_helper_neon_padd_u8,
- gen_helper_neon_padd_u16,
- tcg_gen_add_i32,
- };
- genfn = fns[size];
- break;
- }
- case 0x14: /* SMAXP, UMAXP */
- {
- static NeonGenTwoOpFn * const fns[3][2] = {
- { gen_helper_neon_pmax_s8, gen_helper_neon_pmax_u8 },
- { gen_helper_neon_pmax_s16, gen_helper_neon_pmax_u16 },
- { tcg_gen_smax_i32, tcg_gen_umax_i32 },
- };
- genfn = fns[size][u];
- break;
- }
- case 0x15: /* SMINP, UMINP */
- {
- static NeonGenTwoOpFn * const fns[3][2] = {
- { gen_helper_neon_pmin_s8, gen_helper_neon_pmin_u8 },
- { gen_helper_neon_pmin_s16, gen_helper_neon_pmin_u16 },
- { tcg_gen_smin_i32, tcg_gen_umin_i32 },
- };
- genfn = fns[size][u];
- break;
- }
- /* The FP operations are all on single floats (32 bit) */
- case 0x58: /* FMAXNMP */
- gen_helper_vfp_maxnums(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x5a: /* FADDP */
- gen_helper_vfp_adds(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x5e: /* FMAXP */
- gen_helper_vfp_maxs(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x78: /* FMINNMP */
- gen_helper_vfp_minnums(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x7e: /* FMINP */
- gen_helper_vfp_mins(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- /* FP ops called directly, otherwise call now */
- if (genfn) {
- genfn(tcg_res[pass], tcg_op1, tcg_op2);
- }
- }
-
- for (pass = 0; pass < maxpass; pass++) {
- write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32);
- }
- clear_vec_high(s, is_q, rd);
- }
-}
-
-/* Floating point op subgroup of C3.6.16. */
-static void disas_simd_3same_float(DisasContext *s, uint32_t insn)
-{
- /* For floating point ops, the U, size[1] and opcode bits
- * together indicate the operation. size[0] indicates single
- * or double.
- */
- int fpopcode = extract32(insn, 11, 5)
- | (extract32(insn, 23, 1) << 5)
- | (extract32(insn, 29, 1) << 6);
- int is_q = extract32(insn, 30, 1);
- int size = extract32(insn, 22, 1);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
-
- int datasize = is_q ? 128 : 64;
- int esize = 32 << size;
- int elements = datasize / esize;
-
- if (size == 1 && !is_q) {
- unallocated_encoding(s);
- return;
- }
-
- switch (fpopcode) {
- case 0x58: /* FMAXNMP */
- case 0x5a: /* FADDP */
- case 0x5e: /* FMAXP */
- case 0x78: /* FMINNMP */
- case 0x7e: /* FMINP */
- if (size && !is_q) {
- unallocated_encoding(s);
- return;
- }
- handle_simd_3same_pair(s, is_q, 0, fpopcode, size ? MO_64 : MO_32,
- rn, rm, rd);
- return;
- case 0x1b: /* FMULX */
- case 0x1f: /* FRECPS */
- case 0x3f: /* FRSQRTS */
- case 0x5d: /* FACGE */
- case 0x7d: /* FACGT */
- case 0x19: /* FMLA */
- case 0x39: /* FMLS */
- case 0x18: /* FMAXNM */
- case 0x1a: /* FADD */
- case 0x1c: /* FCMEQ */
- case 0x1e: /* FMAX */
- case 0x38: /* FMINNM */
- case 0x3a: /* FSUB */
- case 0x3e: /* FMIN */
- case 0x5b: /* FMUL */
- case 0x5c: /* FCMGE */
- case 0x5f: /* FDIV */
- case 0x7a: /* FABD */
- case 0x7c: /* FCMGT */
- if (!fp_access_check(s)) {
- return;
- }
- handle_3same_float(s, size, elements, fpopcode, rd, rn, rm);
- return;
-
- case 0x1d: /* FMLAL */
- case 0x3d: /* FMLSL */
- case 0x59: /* FMLAL2 */
- case 0x79: /* FMLSL2 */
- if (size & 1 || !dc_isar_feature(aa64_fhm, s)) {
- unallocated_encoding(s);
- return;
- }
- if (fp_access_check(s)) {
- int is_s = extract32(insn, 23, 1);
- int is_2 = extract32(insn, 29, 1);
- int data = (is_2 << 1) | is_s;
- tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd),
- vec_full_reg_offset(s, rn),
- vec_full_reg_offset(s, rm), tcg_env,
- is_q ? 16 : 8, vec_full_reg_size(s),
- data, gen_helper_gvec_fmlal_a64);
- }
- return;
-
- default:
- unallocated_encoding(s);
- return;
- }
-}
-
/* Integer op subgroup of C3.6.16. */
static void disas_simd_3same_int(DisasContext *s, uint32_t insn)
{
@@ -11365,244 +11177,17 @@ static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn)
int opcode = extract32(insn, 11, 5);
switch (opcode) {
- case 0x3: /* logic ops */
- disas_simd_3same_logic(s, insn);
- break;
- case 0x17: /* ADDP */
- case 0x14: /* SMAXP, UMAXP */
- case 0x15: /* SMINP, UMINP */
- {
- /* Pairwise operations */
- int is_q = extract32(insn, 30, 1);
- int u = extract32(insn, 29, 1);
- int size = extract32(insn, 22, 2);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- if (opcode == 0x17) {
- if (u || (size == 3 && !is_q)) {
- unallocated_encoding(s);
- return;
- }
- } else {
- if (size == 3) {
- unallocated_encoding(s);
- return;
- }
- }
- handle_simd_3same_pair(s, is_q, u, opcode, size, rn, rm, rd);
- break;
- }
- case 0x18 ... 0x31:
- /* floating point ops, sz[1] and U are part of opcode */
- disas_simd_3same_float(s, insn);
- break;
default:
disas_simd_3same_int(s, insn);
break;
- }
-}
-
-/*
- * Advanced SIMD three same (ARMv8.2 FP16 variants)
- *
- * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0
- * +---+---+---+-----------+---------+------+-----+--------+---+------+------+
- * | 0 | Q | U | 0 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd |
- * +---+---+---+-----------+---------+------+-----+--------+---+------+------+
- *
- * This includes FMULX, FCMEQ (register), FRECPS, FRSQRTS, FCMGE
- * (register), FACGE, FABD, FCMGT (register) and FACGT.
- *
- */
-static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn)
-{
- int opcode = extract32(insn, 11, 3);
- int u = extract32(insn, 29, 1);
- int a = extract32(insn, 23, 1);
- int is_q = extract32(insn, 30, 1);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- /*
- * For these floating point ops, the U, a and opcode bits
- * together indicate the operation.
- */
- int fpopcode = opcode | (a << 3) | (u << 4);
- int datasize = is_q ? 128 : 64;
- int elements = datasize / 16;
- bool pairwise;
- TCGv_ptr fpst;
- int pass;
-
- switch (fpopcode) {
- case 0x0: /* FMAXNM */
- case 0x1: /* FMLA */
- case 0x2: /* FADD */
- case 0x3: /* FMULX */
- case 0x4: /* FCMEQ */
- case 0x6: /* FMAX */
- case 0x7: /* FRECPS */
- case 0x8: /* FMINNM */
- case 0x9: /* FMLS */
- case 0xa: /* FSUB */
- case 0xe: /* FMIN */
- case 0xf: /* FRSQRTS */
- case 0x13: /* FMUL */
- case 0x14: /* FCMGE */
- case 0x15: /* FACGE */
- case 0x17: /* FDIV */
- case 0x1a: /* FABD */
- case 0x1c: /* FCMGT */
- case 0x1d: /* FACGT */
- pairwise = false;
- break;
- case 0x10: /* FMAXNMP */
- case 0x12: /* FADDP */
- case 0x16: /* FMAXP */
- case 0x18: /* FMINNMP */
- case 0x1e: /* FMINP */
- pairwise = true;
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!dc_isar_feature(aa64_fp16, s)) {
+ case 0x3: /* logic ops */
+ case 0x14: /* SMAXP, UMAXP */
+ case 0x15: /* SMINP, UMINP */
+ case 0x17: /* ADDP */
+ case 0x18 ... 0x31: /* floating point ops */
unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- fpst = fpstatus_ptr(FPST_FPCR_F16);
-
- if (pairwise) {
- int maxpass = is_q ? 8 : 4;
- TCGv_i32 tcg_op1 = tcg_temp_new_i32();
- TCGv_i32 tcg_op2 = tcg_temp_new_i32();
- TCGv_i32 tcg_res[8];
-
- for (pass = 0; pass < maxpass; pass++) {
- int passreg = pass < (maxpass / 2) ? rn : rm;
- int passelt = (pass << 1) & (maxpass - 1);
-
- read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_16);
- read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_16);
- tcg_res[pass] = tcg_temp_new_i32();
-
- switch (fpopcode) {
- case 0x10: /* FMAXNMP */
- gen_helper_advsimd_maxnumh(tcg_res[pass], tcg_op1, tcg_op2,
- fpst);
- break;
- case 0x12: /* FADDP */
- gen_helper_advsimd_addh(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x16: /* FMAXP */
- gen_helper_advsimd_maxh(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- case 0x18: /* FMINNMP */
- gen_helper_advsimd_minnumh(tcg_res[pass], tcg_op1, tcg_op2,
- fpst);
- break;
- case 0x1e: /* FMINP */
- gen_helper_advsimd_minh(tcg_res[pass], tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- }
-
- for (pass = 0; pass < maxpass; pass++) {
- write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_16);
- }
- } else {
- for (pass = 0; pass < elements; pass++) {
- TCGv_i32 tcg_op1 = tcg_temp_new_i32();
- TCGv_i32 tcg_op2 = tcg_temp_new_i32();
- TCGv_i32 tcg_res = tcg_temp_new_i32();
-
- read_vec_element_i32(s, tcg_op1, rn, pass, MO_16);
- read_vec_element_i32(s, tcg_op2, rm, pass, MO_16);
-
- switch (fpopcode) {
- case 0x0: /* FMAXNM */
- gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1: /* FMLA */
- read_vec_element_i32(s, tcg_res, rd, pass, MO_16);
- gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res,
- fpst);
- break;
- case 0x2: /* FADD */
- gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x3: /* FMULX */
- gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x4: /* FCMEQ */
- gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x6: /* FMAX */
- gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x7: /* FRECPS */
- gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x8: /* FMINNM */
- gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x9: /* FMLS */
- /* As usual for ARM, separate negation for fused multiply-add */
- tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000);
- read_vec_element_i32(s, tcg_res, rd, pass, MO_16);
- gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res,
- fpst);
- break;
- case 0xa: /* FSUB */
- gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xe: /* FMIN */
- gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0xf: /* FRSQRTS */
- gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x13: /* FMUL */
- gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x14: /* FCMGE */
- gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x15: /* FACGE */
- gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x17: /* FDIV */
- gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1a: /* FABD */
- gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst);
- tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff);
- break;
- case 0x1c: /* FCMGT */
- gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- case 0x1d: /* FACGT */
- gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_vec_element_i32(s, tcg_res, rd, pass, MO_16);
- }
+ break;
}
-
- clear_vec_high(s, is_q, rd);
}
/* AdvSIMD three same extra
@@ -12373,10 +11958,10 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
}
break;
case 0x2f: /* FABS */
- gen_helper_vfp_abss(tcg_res, tcg_op);
+ gen_vfp_abss(tcg_res, tcg_op);
break;
case 0x6f: /* FNEG */
- gen_helper_vfp_negs(tcg_res, tcg_op);
+ gen_vfp_negs(tcg_res, tcg_op);
break;
case 0x7f: /* FSQRT */
gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env);
@@ -12811,12 +12396,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
case 0x0c: /* SQDMULH */
case 0x0d: /* SQRDMULH */
break;
- case 0x01: /* FMLA */
- case 0x05: /* FMLS */
- case 0x09: /* FMUL */
- case 0x19: /* FMULX */
- is_fp = 1;
- break;
case 0x1d: /* SQRDMLAH */
case 0x1f: /* SQRDMLSH */
if (!dc_isar_feature(aa64_rdm, s)) {
@@ -12871,38 +12450,23 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
is_fp = 2;
break;
+ default:
case 0x00: /* FMLAL */
+ case 0x01: /* FMLA */
case 0x04: /* FMLSL */
+ case 0x05: /* FMLS */
+ case 0x09: /* FMUL */
case 0x18: /* FMLAL2 */
+ case 0x19: /* FMULX */
case 0x1c: /* FMLSL2 */
- if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_fhm, s)) {
- unallocated_encoding(s);
- return;
- }
- size = MO_16;
- /* is_fp, but we pass tcg_env not fp_status. */
- break;
- default:
unallocated_encoding(s);
return;
}
switch (is_fp) {
case 1: /* normal fp */
- /* convert insn encoded size to MemOp size */
- switch (size) {
- case 0: /* half-precision */
- size = MO_16;
- is_fp16 = true;
- break;
- case MO_32: /* single precision */
- case MO_64: /* double precision */
- break;
- default:
- unallocated_encoding(s);
- return;
- }
- break;
+ unallocated_encoding(s); /* in decodetree */
+ return;
case 2: /* complex fp */
/* Each indexable element is a complex pair. */
@@ -13013,22 +12577,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
return;
- case 0x00: /* FMLAL */
- case 0x04: /* FMLSL */
- case 0x18: /* FMLAL2 */
- case 0x1c: /* FMLSL2 */
- {
- int is_s = extract32(opcode, 2, 1);
- int is_2 = u;
- int data = (index << 2) | (is_2 << 1) | is_s;
- tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd),
- vec_full_reg_offset(s, rn),
- vec_full_reg_offset(s, rm), tcg_env,
- is_q ? 16 : 8, vec_full_reg_size(s),
- data, gen_helper_gvec_fmlal_idx_a64);
- }
- return;
-
case 0x08: /* MUL */
if (!is_long && !is_scalar) {
static gen_helper_gvec_3 * const fns[3] = {
@@ -13081,42 +12629,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
if (size == 3) {
- TCGv_i64 tcg_idx = tcg_temp_new_i64();
- int pass;
-
- assert(is_fp && is_q && !is_long);
-
- read_vec_element(s, tcg_idx, rm, index, MO_64);
-
- for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) {
- TCGv_i64 tcg_op = tcg_temp_new_i64();
- TCGv_i64 tcg_res = tcg_temp_new_i64();
-
- read_vec_element(s, tcg_op, rn, pass, MO_64);
-
- switch (16 * u + opcode) {
- case 0x05: /* FMLS */
- /* As usual for ARM, separate negation for fused multiply-add */
- gen_helper_vfp_negd(tcg_op, tcg_op);
- /* fall through */
- case 0x01: /* FMLA */
- read_vec_element(s, tcg_res, rd, pass, MO_64);
- gen_helper_vfp_muladdd(tcg_res, tcg_op, tcg_idx, tcg_res, fpst);
- break;
- case 0x09: /* FMUL */
- gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst);
- break;
- case 0x19: /* FMULX */
- gen_helper_vfp_mulxd(tcg_res, tcg_op, tcg_idx, fpst);
- break;
- default:
- g_assert_not_reached();
- }
-
- write_vec_element(s, tcg_res, rd, pass, MO_64);
- }
-
- clear_vec_high(s, !is_scalar, rd);
+ g_assert_not_reached();
} else if (!is_long) {
/* 32 bit floating point, or 16 or 32 bit integer.
* For the 16 bit scalar case we use the usual Neon helpers and
@@ -13172,74 +12685,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
genfn(tcg_res, tcg_op, tcg_res);
break;
}
- case 0x05: /* FMLS */
- case 0x01: /* FMLA */
- read_vec_element_i32(s, tcg_res, rd, pass,
- is_scalar ? size : MO_32);
- switch (size) {
- case 1:
- if (opcode == 0x5) {
- /* As usual for ARM, separate negation for fused
- * multiply-add */
- tcg_gen_xori_i32(tcg_op, tcg_op, 0x80008000);
- }
- if (is_scalar) {
- gen_helper_advsimd_muladdh(tcg_res, tcg_op, tcg_idx,
- tcg_res, fpst);
- } else {
- gen_helper_advsimd_muladd2h(tcg_res, tcg_op, tcg_idx,
- tcg_res, fpst);
- }
- break;
- case 2:
- if (opcode == 0x5) {
- /* As usual for ARM, separate negation for
- * fused multiply-add */
- tcg_gen_xori_i32(tcg_op, tcg_op, 0x80000000);
- }
- gen_helper_vfp_muladds(tcg_res, tcg_op, tcg_idx,
- tcg_res, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- break;
- case 0x09: /* FMUL */
- switch (size) {
- case 1:
- if (is_scalar) {
- gen_helper_advsimd_mulh(tcg_res, tcg_op,
- tcg_idx, fpst);
- } else {
- gen_helper_advsimd_mul2h(tcg_res, tcg_op,
- tcg_idx, fpst);
- }
- break;
- case 2:
- gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- break;
- case 0x19: /* FMULX */
- switch (size) {
- case 1:
- if (is_scalar) {
- gen_helper_advsimd_mulxh(tcg_res, tcg_op,
- tcg_idx, fpst);
- } else {
- gen_helper_advsimd_mulx2h(tcg_res, tcg_op,
- tcg_idx, fpst);
- }
- break;
- case 2:
- gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst);
- break;
- default:
- g_assert_not_reached();
- }
- break;
case 0x0c: /* SQDMULH */
if (size == 1) {
gen_helper_neon_qdmulh_s16(tcg_res, tcg_env,
@@ -13281,6 +12726,10 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
break;
default:
+ case 0x01: /* FMLA */
+ case 0x05: /* FMLS */
+ case 0x09: /* FMUL */
+ case 0x19: /* FMULX */
g_assert_not_reached();
}
@@ -13454,461 +12903,6 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
}
-/* Crypto AES
- * 31 24 23 22 21 17 16 12 11 10 9 5 4 0
- * +-----------------+------+-----------+--------+-----+------+------+
- * | 0 1 0 0 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
- * +-----------------+------+-----------+--------+-----+------+------+
- */
-static void disas_crypto_aes(DisasContext *s, uint32_t insn)
-{
- int size = extract32(insn, 22, 2);
- int opcode = extract32(insn, 12, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- gen_helper_gvec_2 *genfn2 = NULL;
- gen_helper_gvec_3 *genfn3 = NULL;
-
- if (!dc_isar_feature(aa64_aes, s) || size != 0) {
- unallocated_encoding(s);
- return;
- }
-
- switch (opcode) {
- case 0x4: /* AESE */
- genfn3 = gen_helper_crypto_aese;
- break;
- case 0x6: /* AESMC */
- genfn2 = gen_helper_crypto_aesmc;
- break;
- case 0x5: /* AESD */
- genfn3 = gen_helper_crypto_aesd;
- break;
- case 0x7: /* AESIMC */
- genfn2 = gen_helper_crypto_aesimc;
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
- if (genfn2) {
- gen_gvec_op2_ool(s, true, rd, rn, 0, genfn2);
- } else {
- gen_gvec_op3_ool(s, true, rd, rd, rn, 0, genfn3);
- }
-}
-
-/* Crypto three-reg SHA
- * 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0
- * +-----------------+------+---+------+---+--------+-----+------+------+
- * | 0 1 0 1 1 1 1 0 | size | 0 | Rm | 0 | opcode | 0 0 | Rn | Rd |
- * +-----------------+------+---+------+---+--------+-----+------+------+
- */
-static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
-{
- int size = extract32(insn, 22, 2);
- int opcode = extract32(insn, 12, 3);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- gen_helper_gvec_3 *genfn;
- bool feature;
-
- if (size != 0) {
- unallocated_encoding(s);
- return;
- }
-
- switch (opcode) {
- case 0: /* SHA1C */
- genfn = gen_helper_crypto_sha1c;
- feature = dc_isar_feature(aa64_sha1, s);
- break;
- case 1: /* SHA1P */
- genfn = gen_helper_crypto_sha1p;
- feature = dc_isar_feature(aa64_sha1, s);
- break;
- case 2: /* SHA1M */
- genfn = gen_helper_crypto_sha1m;
- feature = dc_isar_feature(aa64_sha1, s);
- break;
- case 3: /* SHA1SU0 */
- genfn = gen_helper_crypto_sha1su0;
- feature = dc_isar_feature(aa64_sha1, s);
- break;
- case 4: /* SHA256H */
- genfn = gen_helper_crypto_sha256h;
- feature = dc_isar_feature(aa64_sha256, s);
- break;
- case 5: /* SHA256H2 */
- genfn = gen_helper_crypto_sha256h2;
- feature = dc_isar_feature(aa64_sha256, s);
- break;
- case 6: /* SHA256SU1 */
- genfn = gen_helper_crypto_sha256su1;
- feature = dc_isar_feature(aa64_sha256, s);
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!feature) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
- gen_gvec_op3_ool(s, true, rd, rn, rm, 0, genfn);
-}
-
-/* Crypto two-reg SHA
- * 31 24 23 22 21 17 16 12 11 10 9 5 4 0
- * +-----------------+------+-----------+--------+-----+------+------+
- * | 0 1 0 1 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
- * +-----------------+------+-----------+--------+-----+------+------+
- */
-static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
-{
- int size = extract32(insn, 22, 2);
- int opcode = extract32(insn, 12, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- gen_helper_gvec_2 *genfn;
- bool feature;
-
- if (size != 0) {
- unallocated_encoding(s);
- return;
- }
-
- switch (opcode) {
- case 0: /* SHA1H */
- feature = dc_isar_feature(aa64_sha1, s);
- genfn = gen_helper_crypto_sha1h;
- break;
- case 1: /* SHA1SU1 */
- feature = dc_isar_feature(aa64_sha1, s);
- genfn = gen_helper_crypto_sha1su1;
- break;
- case 2: /* SHA256SU0 */
- feature = dc_isar_feature(aa64_sha256, s);
- genfn = gen_helper_crypto_sha256su0;
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!feature) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
- gen_gvec_op2_ool(s, true, rd, rn, 0, genfn);
-}
-
-static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
-{
- tcg_gen_rotli_i64(d, m, 1);
- tcg_gen_xor_i64(d, d, n);
-}
-
-static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m)
-{
- tcg_gen_rotli_vec(vece, d, m, 1);
- tcg_gen_xor_vec(vece, d, d, n);
-}
-
-void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 };
- static const GVecGen3 op = {
- .fni8 = gen_rax1_i64,
- .fniv = gen_rax1_vec,
- .opt_opc = vecop_list,
- .fno = gen_helper_crypto_rax1,
- .vece = MO_64,
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op);
-}
-
-/* Crypto three-reg SHA512
- * 31 21 20 16 15 14 13 12 11 10 9 5 4 0
- * +-----------------------+------+---+---+-----+--------+------+------+
- * | 1 1 0 0 1 1 1 0 0 1 1 | Rm | 1 | O | 0 0 | opcode | Rn | Rd |
- * +-----------------------+------+---+---+-----+--------+------+------+
- */
-static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn)
-{
- int opcode = extract32(insn, 10, 2);
- int o = extract32(insn, 14, 1);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- bool feature;
- gen_helper_gvec_3 *oolfn = NULL;
- GVecGen3Fn *gvecfn = NULL;
-
- if (o == 0) {
- switch (opcode) {
- case 0: /* SHA512H */
- feature = dc_isar_feature(aa64_sha512, s);
- oolfn = gen_helper_crypto_sha512h;
- break;
- case 1: /* SHA512H2 */
- feature = dc_isar_feature(aa64_sha512, s);
- oolfn = gen_helper_crypto_sha512h2;
- break;
- case 2: /* SHA512SU1 */
- feature = dc_isar_feature(aa64_sha512, s);
- oolfn = gen_helper_crypto_sha512su1;
- break;
- case 3: /* RAX1 */
- feature = dc_isar_feature(aa64_sha3, s);
- gvecfn = gen_gvec_rax1;
- break;
- default:
- g_assert_not_reached();
- }
- } else {
- switch (opcode) {
- case 0: /* SM3PARTW1 */
- feature = dc_isar_feature(aa64_sm3, s);
- oolfn = gen_helper_crypto_sm3partw1;
- break;
- case 1: /* SM3PARTW2 */
- feature = dc_isar_feature(aa64_sm3, s);
- oolfn = gen_helper_crypto_sm3partw2;
- break;
- case 2: /* SM4EKEY */
- feature = dc_isar_feature(aa64_sm4, s);
- oolfn = gen_helper_crypto_sm4ekey;
- break;
- default:
- unallocated_encoding(s);
- return;
- }
- }
-
- if (!feature) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- if (oolfn) {
- gen_gvec_op3_ool(s, true, rd, rn, rm, 0, oolfn);
- } else {
- gen_gvec_fn3(s, true, rd, rn, rm, gvecfn, MO_64);
- }
-}
-
-/* Crypto two-reg SHA512
- * 31 12 11 10 9 5 4 0
- * +-----------------------------------------+--------+------+------+
- * | 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 | opcode | Rn | Rd |
- * +-----------------------------------------+--------+------+------+
- */
-static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn)
-{
- int opcode = extract32(insn, 10, 2);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- bool feature;
-
- switch (opcode) {
- case 0: /* SHA512SU0 */
- feature = dc_isar_feature(aa64_sha512, s);
- break;
- case 1: /* SM4E */
- feature = dc_isar_feature(aa64_sm4, s);
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!feature) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- switch (opcode) {
- case 0: /* SHA512SU0 */
- gen_gvec_op2_ool(s, true, rd, rn, 0, gen_helper_crypto_sha512su0);
- break;
- case 1: /* SM4E */
- gen_gvec_op3_ool(s, true, rd, rd, rn, 0, gen_helper_crypto_sm4e);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
-/* Crypto four-register
- * 31 23 22 21 20 16 15 14 10 9 5 4 0
- * +-------------------+-----+------+---+------+------+------+
- * | 1 1 0 0 1 1 1 0 0 | Op0 | Rm | 0 | Ra | Rn | Rd |
- * +-------------------+-----+------+---+------+------+------+
- */
-static void disas_crypto_four_reg(DisasContext *s, uint32_t insn)
-{
- int op0 = extract32(insn, 21, 2);
- int rm = extract32(insn, 16, 5);
- int ra = extract32(insn, 10, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
- bool feature;
-
- switch (op0) {
- case 0: /* EOR3 */
- case 1: /* BCAX */
- feature = dc_isar_feature(aa64_sha3, s);
- break;
- case 2: /* SM3SS1 */
- feature = dc_isar_feature(aa64_sm3, s);
- break;
- default:
- unallocated_encoding(s);
- return;
- }
-
- if (!feature) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- if (op0 < 2) {
- TCGv_i64 tcg_op1, tcg_op2, tcg_op3, tcg_res[2];
- int pass;
-
- tcg_op1 = tcg_temp_new_i64();
- tcg_op2 = tcg_temp_new_i64();
- tcg_op3 = tcg_temp_new_i64();
- tcg_res[0] = tcg_temp_new_i64();
- tcg_res[1] = tcg_temp_new_i64();
-
- for (pass = 0; pass < 2; pass++) {
- read_vec_element(s, tcg_op1, rn, pass, MO_64);
- read_vec_element(s, tcg_op2, rm, pass, MO_64);
- read_vec_element(s, tcg_op3, ra, pass, MO_64);
-
- if (op0 == 0) {
- /* EOR3 */
- tcg_gen_xor_i64(tcg_res[pass], tcg_op2, tcg_op3);
- } else {
- /* BCAX */
- tcg_gen_andc_i64(tcg_res[pass], tcg_op2, tcg_op3);
- }
- tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1);
- }
- write_vec_element(s, tcg_res[0], rd, 0, MO_64);
- write_vec_element(s, tcg_res[1], rd, 1, MO_64);
- } else {
- TCGv_i32 tcg_op1, tcg_op2, tcg_op3, tcg_res, tcg_zero;
-
- tcg_op1 = tcg_temp_new_i32();
- tcg_op2 = tcg_temp_new_i32();
- tcg_op3 = tcg_temp_new_i32();
- tcg_res = tcg_temp_new_i32();
- tcg_zero = tcg_constant_i32(0);
-
- read_vec_element_i32(s, tcg_op1, rn, 3, MO_32);
- read_vec_element_i32(s, tcg_op2, rm, 3, MO_32);
- read_vec_element_i32(s, tcg_op3, ra, 3, MO_32);
-
- tcg_gen_rotri_i32(tcg_res, tcg_op1, 20);
- tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2);
- tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3);
- tcg_gen_rotri_i32(tcg_res, tcg_res, 25);
-
- write_vec_element_i32(s, tcg_zero, rd, 0, MO_32);
- write_vec_element_i32(s, tcg_zero, rd, 1, MO_32);
- write_vec_element_i32(s, tcg_zero, rd, 2, MO_32);
- write_vec_element_i32(s, tcg_res, rd, 3, MO_32);
- }
-}
-
-/* Crypto XAR
- * 31 21 20 16 15 10 9 5 4 0
- * +-----------------------+------+--------+------+------+
- * | 1 1 0 0 1 1 1 0 1 0 0 | Rm | imm6 | Rn | Rd |
- * +-----------------------+------+--------+------+------+
- */
-static void disas_crypto_xar(DisasContext *s, uint32_t insn)
-{
- int rm = extract32(insn, 16, 5);
- int imm6 = extract32(insn, 10, 6);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
-
- if (!dc_isar_feature(aa64_sha3, s)) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- gen_gvec_xar(MO_64, vec_full_reg_offset(s, rd),
- vec_full_reg_offset(s, rn),
- vec_full_reg_offset(s, rm), imm6, 16,
- vec_full_reg_size(s));
-}
-
-/* Crypto three-reg imm2
- * 31 21 20 16 15 14 13 12 11 10 9 5 4 0
- * +-----------------------+------+-----+------+--------+------+------+
- * | 1 1 0 0 1 1 1 0 0 1 0 | Rm | 1 0 | imm2 | opcode | Rn | Rd |
- * +-----------------------+------+-----+------+--------+------+------+
- */
-static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn)
-{
- static gen_helper_gvec_3 * const fns[4] = {
- gen_helper_crypto_sm3tt1a, gen_helper_crypto_sm3tt1b,
- gen_helper_crypto_sm3tt2a, gen_helper_crypto_sm3tt2b,
- };
- int opcode = extract32(insn, 10, 2);
- int imm2 = extract32(insn, 12, 2);
- int rm = extract32(insn, 16, 5);
- int rn = extract32(insn, 5, 5);
- int rd = extract32(insn, 0, 5);
-
- if (!dc_isar_feature(aa64_sm3, s)) {
- unallocated_encoding(s);
- return;
- }
-
- if (!fp_access_check(s)) {
- return;
- }
-
- gen_gvec_op3_ool(s, true, rd, rn, rm, imm2, fns[opcode]);
-}
-
/* C3.6 Data processing - SIMD, inc Crypto
*
* As the decode gets a little complex we are using a table based
@@ -13921,7 +12915,6 @@ static const AArch64DecodeTable data_proc_simd[] = {
{ 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff },
{ 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc },
{ 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes },
- { 0x0e000400, 0x9fe08400, disas_simd_copy },
{ 0x0f000000, 0x9f000400, disas_simd_indexed }, /* vector indexed */
/* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */
{ 0x0f000400, 0x9ff80400, disas_simd_mod_imm },
@@ -13933,21 +12926,9 @@ static const AArch64DecodeTable data_proc_simd[] = {
{ 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra },
{ 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff },
{ 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc },
- { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise },
- { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy },
{ 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */
{ 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm },
- { 0x4e280800, 0xff3e0c00, disas_crypto_aes },
- { 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha },
- { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha },
- { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 },
- { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 },
- { 0xce000000, 0xff808000, disas_crypto_four_reg },
- { 0xce800000, 0xffe00000, disas_crypto_xar },
- { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 },
- { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 },
{ 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 },
- { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 },
{ 0x00000000, 0x00000000, NULL }
};
@@ -14382,20 +13363,10 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void aarch64_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- DisasContext *dc = container_of(dcbase, DisasContext, base);
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first));
- target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size);
-}
-
const TranslatorOps aarch64_translator_ops = {
.init_disas_context = aarch64_tr_init_disas_context,
.tb_start = aarch64_tr_tb_start,
.insn_start = aarch64_tr_insn_start,
.translate_insn = aarch64_tr_translate_insn,
.tb_stop = aarch64_tr_tb_stop,
- .disas_log = aarch64_tr_disas_log,
};
diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h
index 7b811b8ac5..91750f0ca9 100644
--- a/target/arm/tcg/translate-a64.h
+++ b/target/arm/tcg/translate-a64.h
@@ -193,6 +193,10 @@ void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
uint32_t rm_ofs, int64_t shift,
uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
+ uint32_t a, uint32_t oprsz, uint32_t maxsz);
+void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
+ uint32_t a, uint32_t oprsz, uint32_t maxsz);
void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm);
void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm);
diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c
index 144f18ba22..18b048611b 100644
--- a/target/arm/tcg/translate-neon.c
+++ b/target/arm/tcg/translate-neon.c
@@ -830,6 +830,11 @@ DO_3SAME_NO_SZ_3(VABD_S, gen_gvec_sabd)
DO_3SAME_NO_SZ_3(VABA_S, gen_gvec_saba)
DO_3SAME_NO_SZ_3(VABD_U, gen_gvec_uabd)
DO_3SAME_NO_SZ_3(VABA_U, gen_gvec_uaba)
+DO_3SAME_NO_SZ_3(VPADD, gen_gvec_addp)
+DO_3SAME_NO_SZ_3(VPMAX_S, gen_gvec_smaxp)
+DO_3SAME_NO_SZ_3(VPMIN_S, gen_gvec_sminp)
+DO_3SAME_NO_SZ_3(VPMAX_U, gen_gvec_umaxp)
+DO_3SAME_NO_SZ_3(VPMIN_U, gen_gvec_uminp)
#define DO_3SAME_CMP(INSN, COND) \
static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \
@@ -1002,82 +1007,6 @@ DO_3SAME_32_ENV(VQSHL_U, qshl_u)
DO_3SAME_32_ENV(VQRSHL_S, qrshl_s)
DO_3SAME_32_ENV(VQRSHL_U, qrshl_u)
-static bool do_3same_pair(DisasContext *s, arg_3same *a, NeonGenTwoOpFn *fn)
-{
- /* Operations handled pairwise 32 bits at a time */
- TCGv_i32 tmp, tmp2, tmp3;
-
- if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
- return false;
- }
-
- /* UNDEF accesses to D16-D31 if they don't exist. */
- if (!dc_isar_feature(aa32_simd_r32, s) &&
- ((a->vd | a->vn | a->vm) & 0x10)) {
- return false;
- }
-
- if (a->size == 3) {
- return false;
- }
-
- if (!vfp_access_check(s)) {
- return true;
- }
-
- assert(a->q == 0); /* enforced by decode patterns */
-
- /*
- * Note that we have to be careful not to clobber the source operands
- * in the "vm == vd" case by storing the result of the first pass too
- * early. Since Q is 0 there are always just two passes, so instead
- * of a complicated loop over each pass we just unroll.
- */
- tmp = tcg_temp_new_i32();
- tmp2 = tcg_temp_new_i32();
- tmp3 = tcg_temp_new_i32();
-
- read_neon_element32(tmp, a->vn, 0, MO_32);
- read_neon_element32(tmp2, a->vn, 1, MO_32);
- fn(tmp, tmp, tmp2);
-
- read_neon_element32(tmp3, a->vm, 0, MO_32);
- read_neon_element32(tmp2, a->vm, 1, MO_32);
- fn(tmp3, tmp3, tmp2);
-
- write_neon_element32(tmp, a->vd, 0, MO_32);
- write_neon_element32(tmp3, a->vd, 1, MO_32);
-
- return true;
-}
-
-#define DO_3SAME_PAIR(INSN, func) \
- static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \
- { \
- static NeonGenTwoOpFn * const fns[] = { \
- gen_helper_neon_##func##8, \
- gen_helper_neon_##func##16, \
- gen_helper_neon_##func##32, \
- }; \
- if (a->size > 2) { \
- return false; \
- } \
- return do_3same_pair(s, a, fns[a->size]); \
- }
-
-/* 32-bit pairwise ops end up the same as the elementwise versions. */
-#define gen_helper_neon_pmax_s32 tcg_gen_smax_i32
-#define gen_helper_neon_pmax_u32 tcg_gen_umax_i32
-#define gen_helper_neon_pmin_s32 tcg_gen_smin_i32
-#define gen_helper_neon_pmin_u32 tcg_gen_umin_i32
-#define gen_helper_neon_padd_u32 tcg_gen_add_i32
-
-DO_3SAME_PAIR(VPMAX_S, pmax_s)
-DO_3SAME_PAIR(VPMIN_S, pmin_s)
-DO_3SAME_PAIR(VPMAX_U, pmax_u)
-DO_3SAME_PAIR(VPMIN_U, pmin_u)
-DO_3SAME_PAIR(VPADD, padd_u)
-
#define DO_3SAME_VQDMULH(INSN, FUNC) \
WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \
WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \
@@ -1144,6 +1073,9 @@ DO_3S_FP_GVEC(VFMA, gen_helper_gvec_vfma_s, gen_helper_gvec_vfma_h)
DO_3S_FP_GVEC(VFMS, gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_h)
DO_3S_FP_GVEC(VRECPS, gen_helper_gvec_recps_nf_s, gen_helper_gvec_recps_nf_h)
DO_3S_FP_GVEC(VRSQRTS, gen_helper_gvec_rsqrts_nf_s, gen_helper_gvec_rsqrts_nf_h)
+DO_3S_FP_GVEC(VPADD, gen_helper_gvec_faddp_s, gen_helper_gvec_faddp_h)
+DO_3S_FP_GVEC(VPMAX, gen_helper_gvec_fmaxp_s, gen_helper_gvec_fmaxp_h)
+DO_3S_FP_GVEC(VPMIN, gen_helper_gvec_fminp_s, gen_helper_gvec_fminp_h)
WRAP_FP_GVEC(gen_VMAXNM_fp32_3s, FPST_STD, gen_helper_gvec_fmaxnum_s)
WRAP_FP_GVEC(gen_VMAXNM_fp16_3s, FPST_STD_F16, gen_helper_gvec_fmaxnum_h)
@@ -1180,58 +1112,6 @@ static bool trans_VMINNM_fp_3s(DisasContext *s, arg_3same *a)
return do_3same(s, a, gen_VMINNM_fp32_3s);
}
-static bool do_3same_fp_pair(DisasContext *s, arg_3same *a,
- gen_helper_gvec_3_ptr *fn)
-{
- /* FP pairwise operations */
- TCGv_ptr fpstatus;
-
- if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
- return false;
- }
-
- /* UNDEF accesses to D16-D31 if they don't exist. */
- if (!dc_isar_feature(aa32_simd_r32, s) &&
- ((a->vd | a->vn | a->vm) & 0x10)) {
- return false;
- }
-
- if (!vfp_access_check(s)) {
- return true;
- }
-
- assert(a->q == 0); /* enforced by decode patterns */
-
-
- fpstatus = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD);
- tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd),
- vfp_reg_offset(1, a->vn),
- vfp_reg_offset(1, a->vm),
- fpstatus, 8, 8, 0, fn);
-
- return true;
-}
-
-/*
- * For all the functions using this macro, size == 1 means fp16,
- * which is an architecture extension we don't implement yet.
- */
-#define DO_3S_FP_PAIR(INSN,FUNC) \
- static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \
- { \
- if (a->size == MO_16) { \
- if (!dc_isar_feature(aa32_fp16_arith, s)) { \
- return false; \
- } \
- return do_3same_fp_pair(s, a, FUNC##h); \
- } \
- return do_3same_fp_pair(s, a, FUNC##s); \
- }
-
-DO_3S_FP_PAIR(VPADD, gen_helper_neon_padd)
-DO_3S_FP_PAIR(VPMAX, gen_helper_neon_pmax)
-DO_3S_FP_PAIR(VPMIN, gen_helper_neon_pmin)
-
static bool do_vector_2sh(DisasContext *s, arg_2reg_shift *a, GVecGen2iFn *fn)
{
/* Handle a 2-reg-shift insn which can be vectorized. */
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index ada05aa530..798ab2bfb1 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -527,94 +527,6 @@ TRANS_FEAT(ORR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_or, a)
TRANS_FEAT(EOR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_xor, a)
TRANS_FEAT(BIC_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_andc, a)
-static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
- uint64_t mask = dup_const(MO_8, 0xff >> sh);
-
- tcg_gen_xor_i64(t, n, m);
- tcg_gen_shri_i64(d, t, sh);
- tcg_gen_shli_i64(t, t, 8 - sh);
- tcg_gen_andi_i64(d, d, mask);
- tcg_gen_andi_i64(t, t, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
- uint64_t mask = dup_const(MO_16, 0xffff >> sh);
-
- tcg_gen_xor_i64(t, n, m);
- tcg_gen_shri_i64(d, t, sh);
- tcg_gen_shli_i64(t, t, 16 - sh);
- tcg_gen_andi_i64(d, d, mask);
- tcg_gen_andi_i64(t, t, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh)
-{
- tcg_gen_xor_i32(d, n, m);
- tcg_gen_rotri_i32(d, d, sh);
-}
-
-static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh)
-{
- tcg_gen_xor_i64(d, n, m);
- tcg_gen_rotri_i64(d, d, sh);
-}
-
-static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
- TCGv_vec m, int64_t sh)
-{
- tcg_gen_xor_vec(vece, d, n, m);
- tcg_gen_rotri_vec(vece, d, d, sh);
-}
-
-void gen_gvec_xar(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, int64_t shift,
- uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 };
- static const GVecGen3i ops[4] = {
- { .fni8 = gen_xar8_i64,
- .fniv = gen_xar_vec,
- .fno = gen_helper_sve2_xar_b,
- .opt_opc = vecop,
- .vece = MO_8 },
- { .fni8 = gen_xar16_i64,
- .fniv = gen_xar_vec,
- .fno = gen_helper_sve2_xar_h,
- .opt_opc = vecop,
- .vece = MO_16 },
- { .fni4 = gen_xar_i32,
- .fniv = gen_xar_vec,
- .fno = gen_helper_sve2_xar_s,
- .opt_opc = vecop,
- .vece = MO_32 },
- { .fni8 = gen_xar_i64,
- .fniv = gen_xar_vec,
- .fno = gen_helper_gvec_xar_d,
- .opt_opc = vecop,
- .vece = MO_64 }
- };
- int esize = 8 << vece;
-
- /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */
- tcg_debug_assert(shift >= 0);
- tcg_debug_assert(shift <= esize);
- shift &= esize - 1;
-
- if (shift == 0) {
- /* xar with no rotate devolves to xor. */
- tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz);
- } else {
- tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz,
- shift, &ops[vece]);
- }
-}
-
static bool trans_XAR(DisasContext *s, arg_rrri_esz *a)
{
if (a->esz < 0 || !dc_isar_feature(aa64_sve2, s)) {
@@ -629,61 +541,8 @@ static bool trans_XAR(DisasContext *s, arg_rrri_esz *a)
return true;
}
-static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k)
-{
- tcg_gen_xor_i64(d, n, m);
- tcg_gen_xor_i64(d, d, k);
-}
-
-static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
- TCGv_vec m, TCGv_vec k)
-{
- tcg_gen_xor_vec(vece, d, n, m);
- tcg_gen_xor_vec(vece, d, d, k);
-}
-
-static void gen_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
- uint32_t a, uint32_t oprsz, uint32_t maxsz)
-{
- static const GVecGen4 op = {
- .fni8 = gen_eor3_i64,
- .fniv = gen_eor3_vec,
- .fno = gen_helper_sve2_eor3,
- .vece = MO_64,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- };
- tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op);
-}
-
-TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_eor3, a)
-
-static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k)
-{
- tcg_gen_andc_i64(d, m, k);
- tcg_gen_xor_i64(d, d, n);
-}
-
-static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n,
- TCGv_vec m, TCGv_vec k)
-{
- tcg_gen_andc_vec(vece, d, m, k);
- tcg_gen_xor_vec(vece, d, d, n);
-}
-
-static void gen_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
- uint32_t a, uint32_t oprsz, uint32_t maxsz)
-{
- static const GVecGen4 op = {
- .fni8 = gen_bcax_i64,
- .fniv = gen_bcax_vec,
- .fno = gen_helper_sve2_bcax,
- .vece = MO_64,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- };
- tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op);
-}
-
-TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_bcax, a)
+TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_eor3, a)
+TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_bcax, a)
static void gen_bsl(unsigned vece, uint32_t d, uint32_t n, uint32_t m,
uint32_t a, uint32_t oprsz, uint32_t maxsz)
diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c
index b9af03b7c3..ee53257687 100644
--- a/target/arm/tcg/translate-vfp.c
+++ b/target/arm/tcg/translate-vfp.c
@@ -1763,7 +1763,7 @@ static void gen_VMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_mulh(tmp, vn, vm, fpst);
- gen_helper_vfp_negh(tmp, tmp);
+ gen_vfp_negh(tmp, tmp);
gen_helper_vfp_addh(vd, vd, tmp, fpst);
}
@@ -1781,7 +1781,7 @@ static void gen_VMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_muls(tmp, vn, vm, fpst);
- gen_helper_vfp_negs(tmp, tmp);
+ gen_vfp_negs(tmp, tmp);
gen_helper_vfp_adds(vd, vd, tmp, fpst);
}
@@ -1799,7 +1799,7 @@ static void gen_VMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst)
TCGv_i64 tmp = tcg_temp_new_i64();
gen_helper_vfp_muld(tmp, vn, vm, fpst);
- gen_helper_vfp_negd(tmp, tmp);
+ gen_vfp_negd(tmp, tmp);
gen_helper_vfp_addd(vd, vd, tmp, fpst);
}
@@ -1819,7 +1819,7 @@ static void gen_VNMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_mulh(tmp, vn, vm, fpst);
- gen_helper_vfp_negh(vd, vd);
+ gen_vfp_negh(vd, vd);
gen_helper_vfp_addh(vd, vd, tmp, fpst);
}
@@ -1839,7 +1839,7 @@ static void gen_VNMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_muls(tmp, vn, vm, fpst);
- gen_helper_vfp_negs(vd, vd);
+ gen_vfp_negs(vd, vd);
gen_helper_vfp_adds(vd, vd, tmp, fpst);
}
@@ -1859,7 +1859,7 @@ static void gen_VNMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst)
TCGv_i64 tmp = tcg_temp_new_i64();
gen_helper_vfp_muld(tmp, vn, vm, fpst);
- gen_helper_vfp_negd(vd, vd);
+ gen_vfp_negd(vd, vd);
gen_helper_vfp_addd(vd, vd, tmp, fpst);
}
@@ -1874,8 +1874,8 @@ static void gen_VNMLA_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_mulh(tmp, vn, vm, fpst);
- gen_helper_vfp_negh(tmp, tmp);
- gen_helper_vfp_negh(vd, vd);
+ gen_vfp_negh(tmp, tmp);
+ gen_vfp_negh(vd, vd);
gen_helper_vfp_addh(vd, vd, tmp, fpst);
}
@@ -1890,8 +1890,8 @@ static void gen_VNMLA_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
TCGv_i32 tmp = tcg_temp_new_i32();
gen_helper_vfp_muls(tmp, vn, vm, fpst);
- gen_helper_vfp_negs(tmp, tmp);
- gen_helper_vfp_negs(vd, vd);
+ gen_vfp_negs(tmp, tmp);
+ gen_vfp_negs(vd, vd);
gen_helper_vfp_adds(vd, vd, tmp, fpst);
}
@@ -1906,8 +1906,8 @@ static void gen_VNMLA_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst)
TCGv_i64 tmp = tcg_temp_new_i64();
gen_helper_vfp_muld(tmp, vn, vm, fpst);
- gen_helper_vfp_negd(tmp, tmp);
- gen_helper_vfp_negd(vd, vd);
+ gen_vfp_negd(tmp, tmp);
+ gen_vfp_negd(vd, vd);
gen_helper_vfp_addd(vd, vd, tmp, fpst);
}
@@ -1935,7 +1935,7 @@ static void gen_VNMUL_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
{
/* VNMUL: -(fn * fm) */
gen_helper_vfp_mulh(vd, vn, vm, fpst);
- gen_helper_vfp_negh(vd, vd);
+ gen_vfp_negh(vd, vd);
}
static bool trans_VNMUL_hp(DisasContext *s, arg_VNMUL_sp *a)
@@ -1947,7 +1947,7 @@ static void gen_VNMUL_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst)
{
/* VNMUL: -(fn * fm) */
gen_helper_vfp_muls(vd, vn, vm, fpst);
- gen_helper_vfp_negs(vd, vd);
+ gen_vfp_negs(vd, vd);
}
static bool trans_VNMUL_sp(DisasContext *s, arg_VNMUL_sp *a)
@@ -1959,7 +1959,7 @@ static void gen_VNMUL_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst)
{
/* VNMUL: -(fn * fm) */
gen_helper_vfp_muld(vd, vn, vm, fpst);
- gen_helper_vfp_negd(vd, vd);
+ gen_vfp_negd(vd, vd);
}
static bool trans_VNMUL_dp(DisasContext *s, arg_VNMUL_dp *a)
@@ -2110,12 +2110,12 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d)
vfp_load_reg32(vm, a->vm);
if (neg_n) {
/* VFNMS, VFMS */
- gen_helper_vfp_negh(vn, vn);
+ gen_vfp_negh(vn, vn);
}
vfp_load_reg32(vd, a->vd);
if (neg_d) {
/* VFNMA, VFNMS */
- gen_helper_vfp_negh(vd, vd);
+ gen_vfp_negh(vd, vd);
}
fpst = fpstatus_ptr(FPST_FPCR_F16);
gen_helper_vfp_muladdh(vd, vn, vm, vd, fpst);
@@ -2169,12 +2169,12 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d)
vfp_load_reg32(vm, a->vm);
if (neg_n) {
/* VFNMS, VFMS */
- gen_helper_vfp_negs(vn, vn);
+ gen_vfp_negs(vn, vn);
}
vfp_load_reg32(vd, a->vd);
if (neg_d) {
/* VFNMA, VFNMS */
- gen_helper_vfp_negs(vd, vd);
+ gen_vfp_negs(vd, vd);
}
fpst = fpstatus_ptr(FPST_FPCR);
gen_helper_vfp_muladds(vd, vn, vm, vd, fpst);
@@ -2234,12 +2234,12 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d)
vfp_load_reg64(vm, a->vm);
if (neg_n) {
/* VFNMS, VFMS */
- gen_helper_vfp_negd(vn, vn);
+ gen_vfp_negd(vn, vn);
}
vfp_load_reg64(vd, a->vd);
if (neg_d) {
/* VFNMA, VFNMS */
- gen_helper_vfp_negd(vd, vd);
+ gen_vfp_negd(vd, vd);
}
fpst = fpstatus_ptr(FPST_FPCR);
gen_helper_vfp_muladdd(vd, vn, vm, vd, fpst);
@@ -2409,13 +2409,13 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a)
DO_VFP_VMOV(VMOV_reg, sp, tcg_gen_mov_i32)
DO_VFP_VMOV(VMOV_reg, dp, tcg_gen_mov_i64)
-DO_VFP_2OP(VABS, hp, gen_helper_vfp_absh, aa32_fp16_arith)
-DO_VFP_2OP(VABS, sp, gen_helper_vfp_abss, aa32_fpsp_v2)
-DO_VFP_2OP(VABS, dp, gen_helper_vfp_absd, aa32_fpdp_v2)
+DO_VFP_2OP(VABS, hp, gen_vfp_absh, aa32_fp16_arith)
+DO_VFP_2OP(VABS, sp, gen_vfp_abss, aa32_fpsp_v2)
+DO_VFP_2OP(VABS, dp, gen_vfp_absd, aa32_fpdp_v2)
-DO_VFP_2OP(VNEG, hp, gen_helper_vfp_negh, aa32_fp16_arith)
-DO_VFP_2OP(VNEG, sp, gen_helper_vfp_negs, aa32_fpsp_v2)
-DO_VFP_2OP(VNEG, dp, gen_helper_vfp_negd, aa32_fpdp_v2)
+DO_VFP_2OP(VNEG, hp, gen_vfp_negh, aa32_fp16_arith)
+DO_VFP_2OP(VNEG, sp, gen_vfp_negs, aa32_fpsp_v2)
+DO_VFP_2OP(VNEG, dp, gen_vfp_negd, aa32_fpdp_v2)
static void gen_VSQRT_hp(TCGv_i32 vd, TCGv_i32 vm)
{
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index dc49a8d806..14703d9a6d 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -23,7 +23,6 @@
#include "translate.h"
#include "translate-a32.h"
#include "qemu/log.h"
-#include "disas/disas.h"
#include "arm_ldst.h"
#include "semihosting/semihost.h"
#include "cpregs.h"
@@ -2913,1594 +2912,6 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
gen_rfe(s, pc, load_cpu_field(spsr));
}
-static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs,
- uint32_t opr_sz, uint32_t max_sz,
- gen_helper_gvec_3_ptr *fn)
-{
- TCGv_ptr qc_ptr = tcg_temp_new_ptr();
-
- tcg_gen_addi_ptr(qc_ptr, tcg_env, offsetof(CPUARMState, vfp.qc));
- tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, qc_ptr,
- opr_sz, max_sz, 0, fn);
-}
-
-void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static gen_helper_gvec_3_ptr * const fns[2] = {
- gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32
- };
- tcg_debug_assert(vece >= 1 && vece <= 2);
- gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]);
-}
-
-void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static gen_helper_gvec_3_ptr * const fns[2] = {
- gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32
- };
- tcg_debug_assert(vece >= 1 && vece <= 2);
- gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]);
-}
-
-#define GEN_CMP0(NAME, COND) \
- void NAME(unsigned vece, uint32_t d, uint32_t m, \
- uint32_t opr_sz, uint32_t max_sz) \
- { tcg_gen_gvec_cmpi(COND, vece, d, m, 0, opr_sz, max_sz); }
-
-GEN_CMP0(gen_gvec_ceq0, TCG_COND_EQ)
-GEN_CMP0(gen_gvec_cle0, TCG_COND_LE)
-GEN_CMP0(gen_gvec_cge0, TCG_COND_GE)
-GEN_CMP0(gen_gvec_clt0, TCG_COND_LT)
-GEN_CMP0(gen_gvec_cgt0, TCG_COND_GT)
-
-#undef GEN_CMP0
-
-static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_vec_sar8i_i64(a, a, shift);
- tcg_gen_vec_add8_i64(d, d, a);
-}
-
-static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_vec_sar16i_i64(a, a, shift);
- tcg_gen_vec_add16_i64(d, d, a);
-}
-
-static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
-{
- tcg_gen_sari_i32(a, a, shift);
- tcg_gen_add_i32(d, d, a);
-}
-
-static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_sari_i64(a, a, shift);
- tcg_gen_add_i64(d, d, a);
-}
-
-static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- tcg_gen_sari_vec(vece, a, a, sh);
- tcg_gen_add_vec(vece, d, d, a);
-}
-
-void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sari_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_ssra8_i64,
- .fniv = gen_ssra_vec,
- .fno = gen_helper_gvec_ssra_b,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni8 = gen_ssra16_i64,
- .fniv = gen_ssra_vec,
- .fno = gen_helper_gvec_ssra_h,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_ssra32_i32,
- .fniv = gen_ssra_vec,
- .fno = gen_helper_gvec_ssra_s,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_ssra64_i64,
- .fniv = gen_ssra_vec,
- .fno = gen_helper_gvec_ssra_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize]. */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- /*
- * Shifts larger than the element size are architecturally valid.
- * Signed results in all sign bits.
- */
- shift = MIN(shift, (8 << vece) - 1);
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
-}
-
-static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_vec_shr8i_i64(a, a, shift);
- tcg_gen_vec_add8_i64(d, d, a);
-}
-
-static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_vec_shr16i_i64(a, a, shift);
- tcg_gen_vec_add16_i64(d, d, a);
-}
-
-static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
-{
- tcg_gen_shri_i32(a, a, shift);
- tcg_gen_add_i32(d, d, a);
-}
-
-static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_shri_i64(a, a, shift);
- tcg_gen_add_i64(d, d, a);
-}
-
-static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- tcg_gen_shri_vec(vece, a, a, sh);
- tcg_gen_add_vec(vece, d, d, a);
-}
-
-void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_shri_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_usra8_i64,
- .fniv = gen_usra_vec,
- .fno = gen_helper_gvec_usra_b,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8, },
- { .fni8 = gen_usra16_i64,
- .fniv = gen_usra_vec,
- .fno = gen_helper_gvec_usra_h,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16, },
- { .fni4 = gen_usra32_i32,
- .fniv = gen_usra_vec,
- .fno = gen_helper_gvec_usra_s,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32, },
- { .fni8 = gen_usra64_i64,
- .fniv = gen_usra_vec,
- .fno = gen_helper_gvec_usra_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_64, },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize]. */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- /*
- * Shifts larger than the element size are architecturally valid.
- * Unsigned results in all zeros as input to accumulate: nop.
- */
- if (shift < (8 << vece)) {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- } else {
- /* Nop, but we do need to clear the tail. */
- tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
- }
-}
-
-/*
- * Shift one less than the requested amount, and the low bit is
- * the rounding bit. For the 8 and 16-bit operations, because we
- * mask the low bit, we can perform a normal integer shift instead
- * of a vector shift.
- */
-static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, sh - 1);
- tcg_gen_andi_i64(t, t, dup_const(MO_8, 1));
- tcg_gen_vec_sar8i_i64(d, a, sh);
- tcg_gen_vec_add8_i64(d, d, t);
-}
-
-static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, sh - 1);
- tcg_gen_andi_i64(t, t, dup_const(MO_16, 1));
- tcg_gen_vec_sar16i_i64(d, a, sh);
- tcg_gen_vec_add16_i64(d, d, t);
-}
-
-static void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
-{
- TCGv_i32 t;
-
- /* Handle shift by the input size for the benefit of trans_SRSHR_ri */
- if (sh == 32) {
- tcg_gen_movi_i32(d, 0);
- return;
- }
- t = tcg_temp_new_i32();
- tcg_gen_extract_i32(t, a, sh - 1, 1);
- tcg_gen_sari_i32(d, a, sh);
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_extract_i64(t, a, sh - 1, 1);
- tcg_gen_sari_i64(d, a, sh);
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- TCGv_vec ones = tcg_temp_new_vec_matching(d);
-
- tcg_gen_shri_vec(vece, t, a, sh - 1);
- tcg_gen_dupi_vec(vece, ones, 1);
- tcg_gen_and_vec(vece, t, t, ones);
- tcg_gen_sari_vec(vece, d, a, sh);
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_srshr8_i64,
- .fniv = gen_srshr_vec,
- .fno = gen_helper_gvec_srshr_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni8 = gen_srshr16_i64,
- .fniv = gen_srshr_vec,
- .fno = gen_helper_gvec_srshr_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_srshr32_i32,
- .fniv = gen_srshr_vec,
- .fno = gen_helper_gvec_srshr_s,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_srshr64_i64,
- .fniv = gen_srshr_vec,
- .fno = gen_helper_gvec_srshr_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize] */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- if (shift == (8 << vece)) {
- /*
- * Shifts larger than the element size are architecturally valid.
- * Signed results in all sign bits. With rounding, this produces
- * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0.
- * I.e. always zero.
- */
- tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0);
- } else {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- }
-}
-
-static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- gen_srshr8_i64(t, a, sh);
- tcg_gen_vec_add8_i64(d, d, t);
-}
-
-static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- gen_srshr16_i64(t, a, sh);
- tcg_gen_vec_add16_i64(d, d, t);
-}
-
-static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
-{
- TCGv_i32 t = tcg_temp_new_i32();
-
- gen_srshr32_i32(t, a, sh);
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- gen_srshr64_i64(t, a, sh);
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
-
- gen_srshr_vec(vece, t, a, sh);
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_srsra8_i64,
- .fniv = gen_srsra_vec,
- .fno = gen_helper_gvec_srsra_b,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_8 },
- { .fni8 = gen_srsra16_i64,
- .fniv = gen_srsra_vec,
- .fno = gen_helper_gvec_srsra_h,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_16 },
- { .fni4 = gen_srsra32_i32,
- .fniv = gen_srsra_vec,
- .fno = gen_helper_gvec_srsra_s,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_32 },
- { .fni8 = gen_srsra64_i64,
- .fniv = gen_srsra_vec,
- .fno = gen_helper_gvec_srsra_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize] */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- /*
- * Shifts larger than the element size are architecturally valid.
- * Signed results in all sign bits. With rounding, this produces
- * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0.
- * I.e. always zero. With accumulation, this leaves D unchanged.
- */
- if (shift == (8 << vece)) {
- /* Nop, but we do need to clear the tail. */
- tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
- } else {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- }
-}
-
-static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, sh - 1);
- tcg_gen_andi_i64(t, t, dup_const(MO_8, 1));
- tcg_gen_vec_shr8i_i64(d, a, sh);
- tcg_gen_vec_add8_i64(d, d, t);
-}
-
-static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, sh - 1);
- tcg_gen_andi_i64(t, t, dup_const(MO_16, 1));
- tcg_gen_vec_shr16i_i64(d, a, sh);
- tcg_gen_vec_add16_i64(d, d, t);
-}
-
-static void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
-{
- TCGv_i32 t;
-
- /* Handle shift by the input size for the benefit of trans_URSHR_ri */
- if (sh == 32) {
- tcg_gen_extract_i32(d, a, sh - 1, 1);
- return;
- }
- t = tcg_temp_new_i32();
- tcg_gen_extract_i32(t, a, sh - 1, 1);
- tcg_gen_shri_i32(d, a, sh);
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_extract_i64(t, a, sh - 1, 1);
- tcg_gen_shri_i64(d, a, sh);
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- TCGv_vec ones = tcg_temp_new_vec_matching(d);
-
- tcg_gen_shri_vec(vece, t, a, shift - 1);
- tcg_gen_dupi_vec(vece, ones, 1);
- tcg_gen_and_vec(vece, t, t, ones);
- tcg_gen_shri_vec(vece, d, a, shift);
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_shri_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_urshr8_i64,
- .fniv = gen_urshr_vec,
- .fno = gen_helper_gvec_urshr_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni8 = gen_urshr16_i64,
- .fniv = gen_urshr_vec,
- .fno = gen_helper_gvec_urshr_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_urshr32_i32,
- .fniv = gen_urshr_vec,
- .fno = gen_helper_gvec_urshr_s,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_urshr64_i64,
- .fniv = gen_urshr_vec,
- .fno = gen_helper_gvec_urshr_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize] */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- if (shift == (8 << vece)) {
- /*
- * Shifts larger than the element size are architecturally valid.
- * Unsigned results in zero. With rounding, this produces a
- * copy of the most significant bit.
- */
- tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz);
- } else {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- }
-}
-
-static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- if (sh == 8) {
- tcg_gen_vec_shr8i_i64(t, a, 7);
- } else {
- gen_urshr8_i64(t, a, sh);
- }
- tcg_gen_vec_add8_i64(d, d, t);
-}
-
-static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- if (sh == 16) {
- tcg_gen_vec_shr16i_i64(t, a, 15);
- } else {
- gen_urshr16_i64(t, a, sh);
- }
- tcg_gen_vec_add16_i64(d, d, t);
-}
-
-static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh)
-{
- TCGv_i32 t = tcg_temp_new_i32();
-
- if (sh == 32) {
- tcg_gen_shri_i32(t, a, 31);
- } else {
- gen_urshr32_i32(t, a, sh);
- }
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- if (sh == 64) {
- tcg_gen_shri_i64(t, a, 63);
- } else {
- gen_urshr64_i64(t, a, sh);
- }
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
-
- if (sh == (8 << vece)) {
- tcg_gen_shri_vec(vece, t, a, sh - 1);
- } else {
- gen_urshr_vec(vece, t, a, sh);
- }
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_shri_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen2i ops[4] = {
- { .fni8 = gen_ursra8_i64,
- .fniv = gen_ursra_vec,
- .fno = gen_helper_gvec_ursra_b,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_8 },
- { .fni8 = gen_ursra16_i64,
- .fniv = gen_ursra_vec,
- .fno = gen_helper_gvec_ursra_h,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_16 },
- { .fni4 = gen_ursra32_i32,
- .fniv = gen_ursra_vec,
- .fno = gen_helper_gvec_ursra_s,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_32 },
- { .fni8 = gen_ursra64_i64,
- .fniv = gen_ursra_vec,
- .fno = gen_helper_gvec_ursra_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize] */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
-}
-
-static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- uint64_t mask = dup_const(MO_8, 0xff >> shift);
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, shift);
- tcg_gen_andi_i64(t, t, mask);
- tcg_gen_andi_i64(d, d, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- uint64_t mask = dup_const(MO_16, 0xffff >> shift);
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shri_i64(t, a, shift);
- tcg_gen_andi_i64(t, t, mask);
- tcg_gen_andi_i64(d, d, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
-{
- tcg_gen_shri_i32(a, a, shift);
- tcg_gen_deposit_i32(d, d, a, 0, 32 - shift);
-}
-
-static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_shri_i64(a, a, shift);
- tcg_gen_deposit_i64(d, d, a, 0, 64 - shift);
-}
-
-static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- TCGv_vec m = tcg_temp_new_vec_matching(d);
-
- tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, sh));
- tcg_gen_shri_vec(vece, t, a, sh);
- tcg_gen_and_vec(vece, d, d, m);
- tcg_gen_or_vec(vece, d, d, t);
-}
-
-void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 };
- const GVecGen2i ops[4] = {
- { .fni8 = gen_shr8_ins_i64,
- .fniv = gen_shr_ins_vec,
- .fno = gen_helper_gvec_sri_b,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni8 = gen_shr16_ins_i64,
- .fniv = gen_shr_ins_vec,
- .fno = gen_helper_gvec_sri_h,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_shr32_ins_i32,
- .fniv = gen_shr_ins_vec,
- .fno = gen_helper_gvec_sri_s,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_shr64_ins_i64,
- .fniv = gen_shr_ins_vec,
- .fno = gen_helper_gvec_sri_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [1..esize]. */
- tcg_debug_assert(shift > 0);
- tcg_debug_assert(shift <= (8 << vece));
-
- /* Shift of esize leaves destination unchanged. */
- if (shift < (8 << vece)) {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- } else {
- /* Nop, but we do need to clear the tail. */
- tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz);
- }
-}
-
-static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- uint64_t mask = dup_const(MO_8, 0xff << shift);
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shli_i64(t, a, shift);
- tcg_gen_andi_i64(t, t, mask);
- tcg_gen_andi_i64(d, d, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- uint64_t mask = dup_const(MO_16, 0xffff << shift);
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_shli_i64(t, a, shift);
- tcg_gen_andi_i64(t, t, mask);
- tcg_gen_andi_i64(d, d, ~mask);
- tcg_gen_or_i64(d, d, t);
-}
-
-static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift)
-{
- tcg_gen_deposit_i32(d, d, a, shift, 32 - shift);
-}
-
-static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
-{
- tcg_gen_deposit_i64(d, d, a, shift, 64 - shift);
-}
-
-static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- TCGv_vec m = tcg_temp_new_vec_matching(d);
-
- tcg_gen_shli_vec(vece, t, a, sh);
- tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh));
- tcg_gen_and_vec(vece, d, d, m);
- tcg_gen_or_vec(vece, d, d, t);
-}
-
-void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
- int64_t shift, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 };
- const GVecGen2i ops[4] = {
- { .fni8 = gen_shl8_ins_i64,
- .fniv = gen_shl_ins_vec,
- .fno = gen_helper_gvec_sli_b,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni8 = gen_shl16_ins_i64,
- .fniv = gen_shl_ins_vec,
- .fno = gen_helper_gvec_sli_h,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_shl32_ins_i32,
- .fniv = gen_shl_ins_vec,
- .fno = gen_helper_gvec_sli_s,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_shl64_ins_i64,
- .fniv = gen_shl_ins_vec,
- .fno = gen_helper_gvec_sli_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
-
- /* tszimm encoding produces immediates in the range [0..esize-1]. */
- tcg_debug_assert(shift >= 0);
- tcg_debug_assert(shift < (8 << vece));
-
- if (shift == 0) {
- tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz);
- } else {
- tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]);
- }
-}
-
-static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- gen_helper_neon_mul_u8(a, a, b);
- gen_helper_neon_add_u8(d, d, a);
-}
-
-static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- gen_helper_neon_mul_u8(a, a, b);
- gen_helper_neon_sub_u8(d, d, a);
-}
-
-static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- gen_helper_neon_mul_u16(a, a, b);
- gen_helper_neon_add_u16(d, d, a);
-}
-
-static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- gen_helper_neon_mul_u16(a, a, b);
- gen_helper_neon_sub_u16(d, d, a);
-}
-
-static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- tcg_gen_mul_i32(a, a, b);
- tcg_gen_add_i32(d, d, a);
-}
-
-static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- tcg_gen_mul_i32(a, a, b);
- tcg_gen_sub_i32(d, d, a);
-}
-
-static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- tcg_gen_mul_i64(a, a, b);
- tcg_gen_add_i64(d, d, a);
-}
-
-static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- tcg_gen_mul_i64(a, a, b);
- tcg_gen_sub_i64(d, d, a);
-}
-
-static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- tcg_gen_mul_vec(vece, a, a, b);
- tcg_gen_add_vec(vece, d, d, a);
-}
-
-static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- tcg_gen_mul_vec(vece, a, a, b);
- tcg_gen_sub_vec(vece, d, d, a);
-}
-
-/* Note that while NEON does not support VMLA and VMLS as 64-bit ops,
- * these tables are shared with AArch64 which does support them.
- */
-void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_mul_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fni4 = gen_mla8_i32,
- .fniv = gen_mla_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni4 = gen_mla16_i32,
- .fniv = gen_mla_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_mla32_i32,
- .fniv = gen_mla_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_mla64_i64,
- .fniv = gen_mla_vec,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_mul_vec, INDEX_op_sub_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fni4 = gen_mls8_i32,
- .fniv = gen_mls_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni4 = gen_mls16_i32,
- .fniv = gen_mls_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_mls32_i32,
- .fniv = gen_mls_vec,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_mls64_i64,
- .fniv = gen_mls_vec,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .load_dest = true,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-/* CMTST : test is "if (X & Y != 0)". */
-static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- tcg_gen_and_i32(d, a, b);
- tcg_gen_negsetcond_i32(TCG_COND_NE, d, d, tcg_constant_i32(0));
-}
-
-void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- tcg_gen_and_i64(d, a, b);
- tcg_gen_negsetcond_i64(TCG_COND_NE, d, d, tcg_constant_i64(0));
-}
-
-static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- tcg_gen_and_vec(vece, d, a, b);
- tcg_gen_dupi_vec(vece, a, 0);
- tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a);
-}
-
-void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 };
- static const GVecGen3 ops[4] = {
- { .fni4 = gen_helper_neon_tst_u8,
- .fniv = gen_cmtst_vec,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fni4 = gen_helper_neon_tst_u16,
- .fniv = gen_cmtst_vec,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_cmtst_i32,
- .fniv = gen_cmtst_vec,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_cmtst_i64,
- .fniv = gen_cmtst_vec,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift)
-{
- TCGv_i32 lval = tcg_temp_new_i32();
- TCGv_i32 rval = tcg_temp_new_i32();
- TCGv_i32 lsh = tcg_temp_new_i32();
- TCGv_i32 rsh = tcg_temp_new_i32();
- TCGv_i32 zero = tcg_constant_i32(0);
- TCGv_i32 max = tcg_constant_i32(32);
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_ext8s_i32(lsh, shift);
- tcg_gen_neg_i32(rsh, lsh);
- tcg_gen_shl_i32(lval, src, lsh);
- tcg_gen_shr_i32(rval, src, rsh);
- tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero);
- tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst);
-}
-
-void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift)
-{
- TCGv_i64 lval = tcg_temp_new_i64();
- TCGv_i64 rval = tcg_temp_new_i64();
- TCGv_i64 lsh = tcg_temp_new_i64();
- TCGv_i64 rsh = tcg_temp_new_i64();
- TCGv_i64 zero = tcg_constant_i64(0);
- TCGv_i64 max = tcg_constant_i64(64);
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_ext8s_i64(lsh, shift);
- tcg_gen_neg_i64(rsh, lsh);
- tcg_gen_shl_i64(lval, src, lsh);
- tcg_gen_shr_i64(rval, src, rsh);
- tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero);
- tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst);
-}
-
-static void gen_ushl_vec(unsigned vece, TCGv_vec dst,
- TCGv_vec src, TCGv_vec shift)
-{
- TCGv_vec lval = tcg_temp_new_vec_matching(dst);
- TCGv_vec rval = tcg_temp_new_vec_matching(dst);
- TCGv_vec lsh = tcg_temp_new_vec_matching(dst);
- TCGv_vec rsh = tcg_temp_new_vec_matching(dst);
- TCGv_vec msk, max;
-
- tcg_gen_neg_vec(vece, rsh, shift);
- if (vece == MO_8) {
- tcg_gen_mov_vec(lsh, shift);
- } else {
- msk = tcg_temp_new_vec_matching(dst);
- tcg_gen_dupi_vec(vece, msk, 0xff);
- tcg_gen_and_vec(vece, lsh, shift, msk);
- tcg_gen_and_vec(vece, rsh, rsh, msk);
- }
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_shlv_vec(vece, lval, src, lsh);
- tcg_gen_shrv_vec(vece, rval, src, rsh);
-
- max = tcg_temp_new_vec_matching(dst);
- tcg_gen_dupi_vec(vece, max, 8 << vece);
-
- /*
- * The choice of LT (signed) and GEU (unsigned) are biased toward
- * the instructions of the x86_64 host. For MO_8, the whole byte
- * is significant so we must use an unsigned compare; otherwise we
- * have already masked to a byte and so a signed compare works.
- * Other tcg hosts have a full set of comparisons and do not care.
- */
- if (vece == MO_8) {
- tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max);
- tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max);
- tcg_gen_andc_vec(vece, lval, lval, lsh);
- tcg_gen_andc_vec(vece, rval, rval, rsh);
- } else {
- tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max);
- tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max);
- tcg_gen_and_vec(vece, lval, lval, lsh);
- tcg_gen_and_vec(vece, rval, rval, rsh);
- }
- tcg_gen_or_vec(vece, dst, lval, rval);
-}
-
-void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_neg_vec, INDEX_op_shlv_vec,
- INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_ushl_vec,
- .fno = gen_helper_gvec_ushl_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fniv = gen_ushl_vec,
- .fno = gen_helper_gvec_ushl_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_ushl_i32,
- .fniv = gen_ushl_vec,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_ushl_i64,
- .fniv = gen_ushl_vec,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift)
-{
- TCGv_i32 lval = tcg_temp_new_i32();
- TCGv_i32 rval = tcg_temp_new_i32();
- TCGv_i32 lsh = tcg_temp_new_i32();
- TCGv_i32 rsh = tcg_temp_new_i32();
- TCGv_i32 zero = tcg_constant_i32(0);
- TCGv_i32 max = tcg_constant_i32(31);
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_ext8s_i32(lsh, shift);
- tcg_gen_neg_i32(rsh, lsh);
- tcg_gen_shl_i32(lval, src, lsh);
- tcg_gen_umin_i32(rsh, rsh, max);
- tcg_gen_sar_i32(rval, src, rsh);
- tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero);
- tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval);
-}
-
-void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift)
-{
- TCGv_i64 lval = tcg_temp_new_i64();
- TCGv_i64 rval = tcg_temp_new_i64();
- TCGv_i64 lsh = tcg_temp_new_i64();
- TCGv_i64 rsh = tcg_temp_new_i64();
- TCGv_i64 zero = tcg_constant_i64(0);
- TCGv_i64 max = tcg_constant_i64(63);
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_ext8s_i64(lsh, shift);
- tcg_gen_neg_i64(rsh, lsh);
- tcg_gen_shl_i64(lval, src, lsh);
- tcg_gen_umin_i64(rsh, rsh, max);
- tcg_gen_sar_i64(rval, src, rsh);
- tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero);
- tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval);
-}
-
-static void gen_sshl_vec(unsigned vece, TCGv_vec dst,
- TCGv_vec src, TCGv_vec shift)
-{
- TCGv_vec lval = tcg_temp_new_vec_matching(dst);
- TCGv_vec rval = tcg_temp_new_vec_matching(dst);
- TCGv_vec lsh = tcg_temp_new_vec_matching(dst);
- TCGv_vec rsh = tcg_temp_new_vec_matching(dst);
- TCGv_vec tmp = tcg_temp_new_vec_matching(dst);
-
- /*
- * Rely on the TCG guarantee that out of range shifts produce
- * unspecified results, not undefined behaviour (i.e. no trap).
- * Discard out-of-range results after the fact.
- */
- tcg_gen_neg_vec(vece, rsh, shift);
- if (vece == MO_8) {
- tcg_gen_mov_vec(lsh, shift);
- } else {
- tcg_gen_dupi_vec(vece, tmp, 0xff);
- tcg_gen_and_vec(vece, lsh, shift, tmp);
- tcg_gen_and_vec(vece, rsh, rsh, tmp);
- }
-
- /* Bound rsh so out of bound right shift gets -1. */
- tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1);
- tcg_gen_umin_vec(vece, rsh, rsh, tmp);
- tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp);
-
- tcg_gen_shlv_vec(vece, lval, src, lsh);
- tcg_gen_sarv_vec(vece, rval, src, rsh);
-
- /* Select in-bound left shift. */
- tcg_gen_andc_vec(vece, lval, lval, tmp);
-
- /* Select between left and right shift. */
- if (vece == MO_8) {
- tcg_gen_dupi_vec(vece, tmp, 0);
- tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval);
- } else {
- tcg_gen_dupi_vec(vece, tmp, 0x80);
- tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval);
- }
-}
-
-void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec,
- INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_sshl_vec,
- .fno = gen_helper_gvec_sshl_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fniv = gen_sshl_vec,
- .fno = gen_helper_gvec_sshl_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_sshl_i32,
- .fniv = gen_sshl_vec,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_sshl_i64,
- .fniv = gen_sshl_vec,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
- TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec x = tcg_temp_new_vec_matching(t);
- tcg_gen_add_vec(vece, x, a, b);
- tcg_gen_usadd_vec(vece, t, a, b);
- tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
- tcg_gen_or_vec(vece, sat, sat, x);
-}
-
-void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen4 ops[4] = {
- { .fniv = gen_uqadd_vec,
- .fno = gen_helper_gvec_uqadd_b,
- .write_aofs = true,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fniv = gen_uqadd_vec,
- .fno = gen_helper_gvec_uqadd_h,
- .write_aofs = true,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fniv = gen_uqadd_vec,
- .fno = gen_helper_gvec_uqadd_s,
- .write_aofs = true,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fniv = gen_uqadd_vec,
- .fno = gen_helper_gvec_uqadd_d,
- .write_aofs = true,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
- rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
- TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec x = tcg_temp_new_vec_matching(t);
- tcg_gen_add_vec(vece, x, a, b);
- tcg_gen_ssadd_vec(vece, t, a, b);
- tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
- tcg_gen_or_vec(vece, sat, sat, x);
-}
-
-void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0
- };
- static const GVecGen4 ops[4] = {
- { .fniv = gen_sqadd_vec,
- .fno = gen_helper_gvec_sqadd_b,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_8 },
- { .fniv = gen_sqadd_vec,
- .fno = gen_helper_gvec_sqadd_h,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_16 },
- { .fniv = gen_sqadd_vec,
- .fno = gen_helper_gvec_sqadd_s,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_32 },
- { .fniv = gen_sqadd_vec,
- .fno = gen_helper_gvec_sqadd_d,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_64 },
- };
- tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
- rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
- TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec x = tcg_temp_new_vec_matching(t);
- tcg_gen_sub_vec(vece, x, a, b);
- tcg_gen_ussub_vec(vece, t, a, b);
- tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
- tcg_gen_or_vec(vece, sat, sat, x);
-}
-
-void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0
- };
- static const GVecGen4 ops[4] = {
- { .fniv = gen_uqsub_vec,
- .fno = gen_helper_gvec_uqsub_b,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_8 },
- { .fniv = gen_uqsub_vec,
- .fno = gen_helper_gvec_uqsub_h,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_16 },
- { .fniv = gen_uqsub_vec,
- .fno = gen_helper_gvec_uqsub_s,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_32 },
- { .fniv = gen_uqsub_vec,
- .fno = gen_helper_gvec_uqsub_d,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_64 },
- };
- tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
- rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat,
- TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec x = tcg_temp_new_vec_matching(t);
- tcg_gen_sub_vec(vece, x, a, b);
- tcg_gen_sssub_vec(vece, t, a, b);
- tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t);
- tcg_gen_or_vec(vece, sat, sat, x);
-}
-
-void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0
- };
- static const GVecGen4 ops[4] = {
- { .fniv = gen_sqsub_vec,
- .fno = gen_helper_gvec_sqsub_b,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_8 },
- { .fniv = gen_sqsub_vec,
- .fno = gen_helper_gvec_sqsub_h,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_16 },
- { .fniv = gen_sqsub_vec,
- .fno = gen_helper_gvec_sqsub_s,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_32 },
- { .fniv = gen_sqsub_vec,
- .fno = gen_helper_gvec_sqsub_d,
- .opt_opc = vecop_list,
- .write_aofs = true,
- .vece = MO_64 },
- };
- tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc),
- rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- TCGv_i32 t = tcg_temp_new_i32();
-
- tcg_gen_sub_i32(t, a, b);
- tcg_gen_sub_i32(d, b, a);
- tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t);
-}
-
-static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_sub_i64(t, a, b);
- tcg_gen_sub_i64(d, b, a);
- tcg_gen_movcond_i64(TCG_COND_LT, d, a, b, d, t);
-}
-
-static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
-
- tcg_gen_smin_vec(vece, t, a, b);
- tcg_gen_smax_vec(vece, d, a, b);
- tcg_gen_sub_vec(vece, d, d, t);
-}
-
-void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_sabd_vec,
- .fno = gen_helper_gvec_sabd_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fniv = gen_sabd_vec,
- .fno = gen_helper_gvec_sabd_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_sabd_i32,
- .fniv = gen_sabd_vec,
- .fno = gen_helper_gvec_sabd_s,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_sabd_i64,
- .fniv = gen_sabd_vec,
- .fno = gen_helper_gvec_sabd_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- TCGv_i32 t = tcg_temp_new_i32();
-
- tcg_gen_sub_i32(t, a, b);
- tcg_gen_sub_i32(d, b, a);
- tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, d, t);
-}
-
-static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- TCGv_i64 t = tcg_temp_new_i64();
-
- tcg_gen_sub_i64(t, a, b);
- tcg_gen_sub_i64(d, b, a);
- tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, d, t);
-}
-
-static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
-
- tcg_gen_umin_vec(vece, t, a, b);
- tcg_gen_umax_vec(vece, d, a, b);
- tcg_gen_sub_vec(vece, d, d, t);
-}
-
-void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_uabd_vec,
- .fno = gen_helper_gvec_uabd_b,
- .opt_opc = vecop_list,
- .vece = MO_8 },
- { .fniv = gen_uabd_vec,
- .fno = gen_helper_gvec_uabd_h,
- .opt_opc = vecop_list,
- .vece = MO_16 },
- { .fni4 = gen_uabd_i32,
- .fniv = gen_uabd_vec,
- .fno = gen_helper_gvec_uabd_s,
- .opt_opc = vecop_list,
- .vece = MO_32 },
- { .fni8 = gen_uabd_i64,
- .fniv = gen_uabd_vec,
- .fno = gen_helper_gvec_uabd_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_saba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- TCGv_i32 t = tcg_temp_new_i32();
- gen_sabd_i32(t, a, b);
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_saba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- TCGv_i64 t = tcg_temp_new_i64();
- gen_sabd_i64(t, a, b);
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_saba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- gen_sabd_vec(vece, t, a, b);
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sub_vec, INDEX_op_add_vec,
- INDEX_op_smin_vec, INDEX_op_smax_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_saba_vec,
- .fno = gen_helper_gvec_saba_b,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_8 },
- { .fniv = gen_saba_vec,
- .fno = gen_helper_gvec_saba_h,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_16 },
- { .fni4 = gen_saba_i32,
- .fniv = gen_saba_vec,
- .fno = gen_helper_gvec_saba_s,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_32 },
- { .fni8 = gen_saba_i64,
- .fniv = gen_saba_vec,
- .fno = gen_helper_gvec_saba_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
-static void gen_uaba_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b)
-{
- TCGv_i32 t = tcg_temp_new_i32();
- gen_uabd_i32(t, a, b);
- tcg_gen_add_i32(d, d, t);
-}
-
-static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
-{
- TCGv_i64 t = tcg_temp_new_i64();
- gen_uabd_i64(t, a, b);
- tcg_gen_add_i64(d, d, t);
-}
-
-static void gen_uaba_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b)
-{
- TCGv_vec t = tcg_temp_new_vec_matching(d);
- gen_uabd_vec(vece, t, a, b);
- tcg_gen_add_vec(vece, d, d, t);
-}
-
-void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
- uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz)
-{
- static const TCGOpcode vecop_list[] = {
- INDEX_op_sub_vec, INDEX_op_add_vec,
- INDEX_op_umin_vec, INDEX_op_umax_vec, 0
- };
- static const GVecGen3 ops[4] = {
- { .fniv = gen_uaba_vec,
- .fno = gen_helper_gvec_uaba_b,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_8 },
- { .fniv = gen_uaba_vec,
- .fno = gen_helper_gvec_uaba_h,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_16 },
- { .fni4 = gen_uaba_i32,
- .fniv = gen_uaba_vec,
- .fno = gen_helper_gvec_uaba_s,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_32 },
- { .fni8 = gen_uaba_i64,
- .fniv = gen_uaba_vec,
- .fno = gen_helper_gvec_uaba_d,
- .prefer_i64 = TCG_TARGET_REG_BITS == 64,
- .opt_opc = vecop_list,
- .load_dest = true,
- .vece = MO_64 },
- };
- tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
-}
-
static bool aa32_cpreg_encoding_in_impdef_space(uint8_t crn, uint8_t crm)
{
static const uint16_t mask[3] = {
@@ -9663,22 +8074,12 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void arm_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- DisasContext *dc = container_of(dcbase, DisasContext, base);
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first));
- target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size);
-}
-
static const TranslatorOps arm_translator_ops = {
.init_disas_context = arm_tr_init_disas_context,
.tb_start = arm_tr_tb_start,
.insn_start = arm_tr_insn_start,
.translate_insn = arm_tr_translate_insn,
.tb_stop = arm_tr_tb_stop,
- .disas_log = arm_tr_disas_log,
};
static const TranslatorOps thumb_translator_ops = {
@@ -9687,7 +8088,6 @@ static const TranslatorOps thumb_translator_ops = {
.insn_start = arm_tr_insn_start,
.translate_insn = thumb_tr_translate_insn,
.tb_stop = arm_tr_tb_stop,
- .disas_log = arm_tr_disas_log,
};
/* generate intermediate code for basic block 'tb'. */
diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h
index dc66ff2190..3abdbedfe5 100644
--- a/target/arm/tcg/translate.h
+++ b/target/arm/tcg/translate.h
@@ -252,6 +252,11 @@ static inline int shl_12(DisasContext *s, int x)
return x << 12;
}
+static inline int xor_2(DisasContext *s, int x)
+{
+ return x ^ 2;
+}
+
static inline int neon_3same_fp_size(DisasContext *s, int x)
{
/* Convert 0==fp32, 1==fp16 into a MO_* value */
@@ -401,6 +406,36 @@ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex)
*/
uint64_t vfp_expand_imm(int size, uint8_t imm8);
+static inline void gen_vfp_absh(TCGv_i32 d, TCGv_i32 s)
+{
+ tcg_gen_andi_i32(d, s, INT16_MAX);
+}
+
+static inline void gen_vfp_abss(TCGv_i32 d, TCGv_i32 s)
+{
+ tcg_gen_andi_i32(d, s, INT32_MAX);
+}
+
+static inline void gen_vfp_absd(TCGv_i64 d, TCGv_i64 s)
+{
+ tcg_gen_andi_i64(d, s, INT64_MAX);
+}
+
+static inline void gen_vfp_negh(TCGv_i32 d, TCGv_i32 s)
+{
+ tcg_gen_xori_i32(d, s, 1u << 15);
+}
+
+static inline void gen_vfp_negs(TCGv_i32 d, TCGv_i32 s)
+{
+ tcg_gen_xori_i32(d, s, 1u << 31);
+}
+
+static inline void gen_vfp_negd(TCGv_i64 d, TCGv_i64 s)
+{
+ tcg_gen_xori_i64(d, s, 1ull << 63);
+}
+
/* Vector operations shared between ARM and AArch64. */
void gen_gvec_ceq0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
uint32_t opr_sz, uint32_t max_sz);
@@ -445,6 +480,11 @@ void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
int64_t shift, uint32_t opr_sz, uint32_t max_sz);
+void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh);
+void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh);
+void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh);
+void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh);
+
void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
int64_t shift, uint32_t opr_sz, uint32_t max_sz);
void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs,
@@ -474,6 +514,17 @@ void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs,
+ uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz);
+
/*
* Forward to the isar_feature_* tests given a DisasContext pointer.
*/
diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c
index 1f93510b85..56fea14edb 100644
--- a/target/arm/tcg/vec_helper.c
+++ b/target/arm/tcg/vec_helper.c
@@ -971,6 +971,11 @@ static uint32_t float32_ceq(float32 op1, float32 op2, float_status *stat)
return -float32_eq_quiet(op1, op2, stat);
}
+static uint64_t float64_ceq(float64 op1, float64 op2, float_status *stat)
+{
+ return -float64_eq_quiet(op1, op2, stat);
+}
+
static uint16_t float16_cge(float16 op1, float16 op2, float_status *stat)
{
return -float16_le(op2, op1, stat);
@@ -981,6 +986,11 @@ static uint32_t float32_cge(float32 op1, float32 op2, float_status *stat)
return -float32_le(op2, op1, stat);
}
+static uint64_t float64_cge(float64 op1, float64 op2, float_status *stat)
+{
+ return -float64_le(op2, op1, stat);
+}
+
static uint16_t float16_cgt(float16 op1, float16 op2, float_status *stat)
{
return -float16_lt(op2, op1, stat);
@@ -991,6 +1001,11 @@ static uint32_t float32_cgt(float32 op1, float32 op2, float_status *stat)
return -float32_lt(op2, op1, stat);
}
+static uint64_t float64_cgt(float64 op1, float64 op2, float_status *stat)
+{
+ return -float64_lt(op2, op1, stat);
+}
+
static uint16_t float16_acge(float16 op1, float16 op2, float_status *stat)
{
return -float16_le(float16_abs(op2), float16_abs(op1), stat);
@@ -1001,6 +1016,11 @@ static uint32_t float32_acge(float32 op1, float32 op2, float_status *stat)
return -float32_le(float32_abs(op2), float32_abs(op1), stat);
}
+static uint64_t float64_acge(float64 op1, float64 op2, float_status *stat)
+{
+ return -float64_le(float64_abs(op2), float64_abs(op1), stat);
+}
+
static uint16_t float16_acgt(float16 op1, float16 op2, float_status *stat)
{
return -float16_lt(float16_abs(op2), float16_abs(op1), stat);
@@ -1011,6 +1031,11 @@ static uint32_t float32_acgt(float32 op1, float32 op2, float_status *stat)
return -float32_lt(float32_abs(op2), float32_abs(op1), stat);
}
+static uint64_t float64_acgt(float64 op1, float64 op2, float_status *stat)
+{
+ return -float64_lt(float64_abs(op2), float64_abs(op1), stat);
+}
+
static int16_t vfp_tosszh(float16 x, void *fpstp)
{
float_status *fpst = fpstp;
@@ -1129,6 +1154,11 @@ static float32 float32_abd(float32 op1, float32 op2, float_status *stat)
return float32_abs(float32_sub(op1, op2, stat));
}
+static float64 float64_abd(float64 op1, float64 op2, float_status *stat)
+{
+ return float64_abs(float64_sub(op1, op2, stat));
+}
+
/*
* Reciprocal step. These are the AArch32 version which uses a
* non-fused multiply-and-subtract.
@@ -1213,33 +1243,43 @@ DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64)
DO_3OP(gvec_fabd_h, float16_abd, float16)
DO_3OP(gvec_fabd_s, float32_abd, float32)
+DO_3OP(gvec_fabd_d, float64_abd, float64)
DO_3OP(gvec_fceq_h, float16_ceq, float16)
DO_3OP(gvec_fceq_s, float32_ceq, float32)
+DO_3OP(gvec_fceq_d, float64_ceq, float64)
DO_3OP(gvec_fcge_h, float16_cge, float16)
DO_3OP(gvec_fcge_s, float32_cge, float32)
+DO_3OP(gvec_fcge_d, float64_cge, float64)
DO_3OP(gvec_fcgt_h, float16_cgt, float16)
DO_3OP(gvec_fcgt_s, float32_cgt, float32)
+DO_3OP(gvec_fcgt_d, float64_cgt, float64)
DO_3OP(gvec_facge_h, float16_acge, float16)
DO_3OP(gvec_facge_s, float32_acge, float32)
+DO_3OP(gvec_facge_d, float64_acge, float64)
DO_3OP(gvec_facgt_h, float16_acgt, float16)
DO_3OP(gvec_facgt_s, float32_acgt, float32)
+DO_3OP(gvec_facgt_d, float64_acgt, float64)
DO_3OP(gvec_fmax_h, float16_max, float16)
DO_3OP(gvec_fmax_s, float32_max, float32)
+DO_3OP(gvec_fmax_d, float64_max, float64)
DO_3OP(gvec_fmin_h, float16_min, float16)
DO_3OP(gvec_fmin_s, float32_min, float32)
+DO_3OP(gvec_fmin_d, float64_min, float64)
DO_3OP(gvec_fmaxnum_h, float16_maxnum, float16)
DO_3OP(gvec_fmaxnum_s, float32_maxnum, float32)
+DO_3OP(gvec_fmaxnum_d, float64_maxnum, float64)
DO_3OP(gvec_fminnum_h, float16_minnum, float16)
DO_3OP(gvec_fminnum_s, float32_minnum, float32)
+DO_3OP(gvec_fminnum_d, float64_minnum, float64)
DO_3OP(gvec_recps_nf_h, float16_recps_nf, float16)
DO_3OP(gvec_recps_nf_s, float32_recps_nf, float32)
@@ -1248,6 +1288,13 @@ DO_3OP(gvec_rsqrts_nf_h, float16_rsqrts_nf, float16)
DO_3OP(gvec_rsqrts_nf_s, float32_rsqrts_nf, float32)
#ifdef TARGET_AARCH64
+DO_3OP(gvec_fdiv_h, float16_div, float16)
+DO_3OP(gvec_fdiv_s, float32_div, float32)
+DO_3OP(gvec_fdiv_d, float64_div, float64)
+
+DO_3OP(gvec_fmulx_h, helper_advsimd_mulxh, float16)
+DO_3OP(gvec_fmulx_s, helper_vfp_mulxs, float32)
+DO_3OP(gvec_fmulx_d, helper_vfp_mulxd, float64)
DO_3OP(gvec_recps_h, helper_recpsf_f16, float16)
DO_3OP(gvec_recps_s, helper_recpsf_f32, float32)
@@ -1298,6 +1345,12 @@ static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2,
return float32_muladd(op1, op2, dest, 0, stat);
}
+static float64 float64_muladd_f(float64 dest, float64 op1, float64 op2,
+ float_status *stat)
+{
+ return float64_muladd(op1, op2, dest, 0, stat);
+}
+
static float16 float16_mulsub_f(float16 dest, float16 op1, float16 op2,
float_status *stat)
{
@@ -1310,6 +1363,12 @@ static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2,
return float32_muladd(float32_chs(op1), op2, dest, 0, stat);
}
+static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2,
+ float_status *stat)
+{
+ return float64_muladd(float64_chs(op1), op2, dest, 0, stat);
+}
+
#define DO_MULADD(NAME, FUNC, TYPE) \
void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \
{ \
@@ -1329,9 +1388,11 @@ DO_MULADD(gvec_fmls_s, float32_mulsub_nf, float32)
DO_MULADD(gvec_vfma_h, float16_muladd_f, float16)
DO_MULADD(gvec_vfma_s, float32_muladd_f, float32)
+DO_MULADD(gvec_vfma_d, float64_muladd_f, float64)
DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16)
DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32)
+DO_MULADD(gvec_vfms_d, float64_mulsub_f, float64)
/* For the indexed ops, SVE applies the index per 128-bit vector segment.
* For AdvSIMD, there is of course only one such vector segment.
@@ -1385,7 +1446,7 @@ DO_MLA_IDX(gvec_mls_idx_d, uint64_t, -, H8)
#undef DO_MLA_IDX
-#define DO_FMUL_IDX(NAME, ADD, TYPE, H) \
+#define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \
void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \
{ \
intptr_t i, j, oprsz = simd_oprsz(desc); \
@@ -1395,33 +1456,37 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \
for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \
TYPE mm = m[H(i + idx)]; \
for (j = 0; j < segment; j++) { \
- d[i + j] = TYPE##_##ADD(d[i + j], \
- TYPE##_mul(n[i + j], mm, stat), stat); \
+ d[i + j] = ADD(d[i + j], MUL(n[i + j], mm, stat), stat); \
} \
} \
clear_tail(d, oprsz, simd_maxsz(desc)); \
}
-#define float16_nop(N, M, S) (M)
-#define float32_nop(N, M, S) (M)
-#define float64_nop(N, M, S) (M)
+#define nop(N, M, S) (M)
+
+DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16_mul, float16, H2)
+DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32_mul, float32, H4)
+DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64_mul, float64, H8)
+
+#ifdef TARGET_AARCH64
+
+DO_FMUL_IDX(gvec_fmulx_idx_h, nop, helper_advsimd_mulxh, float16, H2)
+DO_FMUL_IDX(gvec_fmulx_idx_s, nop, helper_vfp_mulxs, float32, H4)
+DO_FMUL_IDX(gvec_fmulx_idx_d, nop, helper_vfp_mulxd, float64, H8)
+
+#endif
-DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16, H2)
-DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32, H4)
-DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64, H8)
+#undef nop
/*
* Non-fused multiply-accumulate operations, for Neon. NB that unlike
* the fused ops below they assume accumulate both from and into Vd.
*/
-DO_FMUL_IDX(gvec_fmla_nf_idx_h, add, float16, H2)
-DO_FMUL_IDX(gvec_fmla_nf_idx_s, add, float32, H4)
-DO_FMUL_IDX(gvec_fmls_nf_idx_h, sub, float16, H2)
-DO_FMUL_IDX(gvec_fmls_nf_idx_s, sub, float32, H4)
-
-#undef float16_nop
-#undef float32_nop
-#undef float64_nop
+DO_FMUL_IDX(gvec_fmla_nf_idx_h, float16_add, float16_mul, float16, H2)
+DO_FMUL_IDX(gvec_fmla_nf_idx_s, float32_add, float32_mul, float32, H4)
+DO_FMUL_IDX(gvec_fmls_nf_idx_h, float16_sub, float16_mul, float16, H2)
+DO_FMUL_IDX(gvec_fmls_nf_idx_s, float32_sub, float32_mul, float32, H4)
+
#undef DO_FMUL_IDX
#define DO_FMLA_IDX(NAME, TYPE, H) \
@@ -2127,50 +2192,90 @@ DO_ABA(gvec_uaba_d, uint64_t)
#undef DO_ABA
-#define DO_NEON_PAIRWISE(NAME, OP) \
- void HELPER(NAME##s)(void *vd, void *vn, void *vm, \
- void *stat, uint32_t oprsz) \
- { \
- float_status *fpst = stat; \
- float32 *d = vd; \
- float32 *n = vn; \
- float32 *m = vm; \
- float32 r0, r1; \
- \
- /* Read all inputs before writing outputs in case vm == vd */ \
- r0 = float32_##OP(n[H4(0)], n[H4(1)], fpst); \
- r1 = float32_##OP(m[H4(0)], m[H4(1)], fpst); \
- \
- d[H4(0)] = r0; \
- d[H4(1)] = r1; \
- } \
- \
- void HELPER(NAME##h)(void *vd, void *vn, void *vm, \
- void *stat, uint32_t oprsz) \
- { \
- float_status *fpst = stat; \
- float16 *d = vd; \
- float16 *n = vn; \
- float16 *m = vm; \
- float16 r0, r1, r2, r3; \
- \
- /* Read all inputs before writing outputs in case vm == vd */ \
- r0 = float16_##OP(n[H2(0)], n[H2(1)], fpst); \
- r1 = float16_##OP(n[H2(2)], n[H2(3)], fpst); \
- r2 = float16_##OP(m[H2(0)], m[H2(1)], fpst); \
- r3 = float16_##OP(m[H2(2)], m[H2(3)], fpst); \
- \
- d[H2(0)] = r0; \
- d[H2(1)] = r1; \
- d[H2(2)] = r2; \
- d[H2(3)] = r3; \
- }
-
-DO_NEON_PAIRWISE(neon_padd, add)
-DO_NEON_PAIRWISE(neon_pmax, max)
-DO_NEON_PAIRWISE(neon_pmin, min)
-
-#undef DO_NEON_PAIRWISE
+#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \
+void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \
+{ \
+ ARMVectorReg scratch; \
+ intptr_t oprsz = simd_oprsz(desc); \
+ intptr_t half = oprsz / sizeof(TYPE) / 2; \
+ TYPE *d = vd, *n = vn, *m = vm; \
+ if (unlikely(d == m)) { \
+ m = memcpy(&scratch, m, oprsz); \
+ } \
+ for (intptr_t i = 0; i < half; ++i) { \
+ d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)], stat); \
+ } \
+ for (intptr_t i = 0; i < half; ++i) { \
+ d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)], stat); \
+ } \
+ clear_tail(d, oprsz, simd_maxsz(desc)); \
+}
+
+DO_3OP_PAIR(gvec_faddp_h, float16_add, float16, H2)
+DO_3OP_PAIR(gvec_faddp_s, float32_add, float32, H4)
+DO_3OP_PAIR(gvec_faddp_d, float64_add, float64, )
+
+DO_3OP_PAIR(gvec_fmaxp_h, float16_max, float16, H2)
+DO_3OP_PAIR(gvec_fmaxp_s, float32_max, float32, H4)
+DO_3OP_PAIR(gvec_fmaxp_d, float64_max, float64, )
+
+DO_3OP_PAIR(gvec_fminp_h, float16_min, float16, H2)
+DO_3OP_PAIR(gvec_fminp_s, float32_min, float32, H4)
+DO_3OP_PAIR(gvec_fminp_d, float64_min, float64, )
+
+DO_3OP_PAIR(gvec_fmaxnump_h, float16_maxnum, float16, H2)
+DO_3OP_PAIR(gvec_fmaxnump_s, float32_maxnum, float32, H4)
+DO_3OP_PAIR(gvec_fmaxnump_d, float64_maxnum, float64, )
+
+DO_3OP_PAIR(gvec_fminnump_h, float16_minnum, float16, H2)
+DO_3OP_PAIR(gvec_fminnump_s, float32_minnum, float32, H4)
+DO_3OP_PAIR(gvec_fminnump_d, float64_minnum, float64, )
+
+#undef DO_3OP_PAIR
+
+#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \
+void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \
+{ \
+ ARMVectorReg scratch; \
+ intptr_t oprsz = simd_oprsz(desc); \
+ intptr_t half = oprsz / sizeof(TYPE) / 2; \
+ TYPE *d = vd, *n = vn, *m = vm; \
+ if (unlikely(d == m)) { \
+ m = memcpy(&scratch, m, oprsz); \
+ } \
+ for (intptr_t i = 0; i < half; ++i) { \
+ d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)]); \
+ } \
+ for (intptr_t i = 0; i < half; ++i) { \
+ d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)]); \
+ } \
+ clear_tail(d, oprsz, simd_maxsz(desc)); \
+}
+
+#define ADD(A, B) (A + B)
+DO_3OP_PAIR(gvec_addp_b, ADD, uint8_t, H1)
+DO_3OP_PAIR(gvec_addp_h, ADD, uint16_t, H2)
+DO_3OP_PAIR(gvec_addp_s, ADD, uint32_t, H4)
+DO_3OP_PAIR(gvec_addp_d, ADD, uint64_t, )
+#undef ADD
+
+DO_3OP_PAIR(gvec_smaxp_b, MAX, int8_t, H1)
+DO_3OP_PAIR(gvec_smaxp_h, MAX, int16_t, H2)
+DO_3OP_PAIR(gvec_smaxp_s, MAX, int32_t, H4)
+
+DO_3OP_PAIR(gvec_umaxp_b, MAX, uint8_t, H1)
+DO_3OP_PAIR(gvec_umaxp_h, MAX, uint16_t, H2)
+DO_3OP_PAIR(gvec_umaxp_s, MAX, uint32_t, H4)
+
+DO_3OP_PAIR(gvec_sminp_b, MIN, int8_t, H1)
+DO_3OP_PAIR(gvec_sminp_h, MIN, int16_t, H2)
+DO_3OP_PAIR(gvec_sminp_s, MIN, int32_t, H4)
+
+DO_3OP_PAIR(gvec_uminp_b, MIN, uint8_t, H1)
+DO_3OP_PAIR(gvec_uminp_h, MIN, uint16_t, H2)
+DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4)
+
+#undef DO_3OP_PAIR
#define DO_VCVT_FIXED(NAME, FUNC, TYPE) \
void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \
diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c
index 3e5e37abbe..ce26b8a71a 100644
--- a/target/arm/vfp_helper.c
+++ b/target/arm/vfp_helper.c
@@ -281,36 +281,6 @@ VFP_BINOP(minnum)
VFP_BINOP(maxnum)
#undef VFP_BINOP
-dh_ctype_f16 VFP_HELPER(neg, h)(dh_ctype_f16 a)
-{
- return float16_chs(a);
-}
-
-float32 VFP_HELPER(neg, s)(float32 a)
-{
- return float32_chs(a);
-}
-
-float64 VFP_HELPER(neg, d)(float64 a)
-{
- return float64_chs(a);
-}
-
-dh_ctype_f16 VFP_HELPER(abs, h)(dh_ctype_f16 a)
-{
- return float16_abs(a);
-}
-
-float32 VFP_HELPER(abs, s)(float32 a)
-{
- return float32_abs(a);
-}
-
-float64 VFP_HELPER(abs, d)(float64 a)
-{
- return float64_abs(a);
-}
-
dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, CPUARMState *env)
{
return float16_sqrt(a, &env->vfp.fp_status_f16);
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
index 71ce62a4c2..f53e1192b1 100644
--- a/target/avr/cpu.c
+++ b/target/avr/cpu.c
@@ -55,7 +55,7 @@ static int avr_cpu_mmu_index(CPUState *cs, bool ifetch)
static void avr_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu_env(cs)->pc_w = tb->pc / 2; /* internally PC points to words */
}
diff --git a/target/avr/helper.c b/target/avr/helper.c
index eeca415c43..345708a1b3 100644
--- a/target/avr/helper.c
+++ b/target/avr/helper.c
@@ -24,6 +24,7 @@
#include "cpu.h"
#include "hw/core/tcg-cpu-ops.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/cpu_ldst.h"
#include "exec/address-spaces.h"
#include "exec/helper-proto.h"
diff --git a/target/avr/translate.c b/target/avr/translate.c
index 87e2bd5ef1..2d51892115 100644
--- a/target/avr/translate.c
+++ b/target/avr/translate.c
@@ -24,7 +24,6 @@
#include "cpu.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
-#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
#include "exec/log.h"
@@ -173,7 +172,7 @@ static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
static uint16_t next_word(DisasContext *ctx)
{
- return cpu_lduw_code(ctx->env, ctx->npc++ * 2);
+ return translator_lduw(ctx->env, &ctx->base, ctx->npc++ * 2);
}
static int append_16(DisasContext *ctx, int x)
@@ -2787,20 +2786,12 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void avr_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps avr_tr_ops = {
.init_disas_context = avr_tr_init_disas_context,
.tb_start = avr_tr_tb_start,
.insn_start = avr_tr_insn_start,
.translate_insn = avr_tr_translate_insn,
.tb_stop = avr_tr_tb_stop,
- .disas_log = avr_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/cris/mmu.c b/target/cris/mmu.c
index b574ec6e5b..d51008c541 100644
--- a/target/cris/mmu.c
+++ b/target/cris/mmu.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "mmu.h"
#ifdef DEBUG
@@ -333,7 +334,7 @@ int cris_mmu_translate(struct cris_mmu_result *res,
if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) {
res->phy = vaddr;
- res->prot = PAGE_BITS;
+ res->prot = PAGE_RWX;
goto done;
}
@@ -344,7 +345,7 @@ int cris_mmu_translate(struct cris_mmu_result *res,
miss = 0;
base = cris_mmu_translate_seg(env, seg);
res->phy = base | (0x0fffffff & vaddr);
- res->prot = PAGE_BITS;
+ res->prot = PAGE_RWX;
} else {
miss = cris_mmu_translate_page(res, env, vaddr, access_type,
is_user, debug);
diff --git a/target/cris/translate.c b/target/cris/translate.c
index b3a4d61d0a..a30c67eb07 100644
--- a/target/cris/translate.c
+++ b/target/cris/translate.c
@@ -25,12 +25,10 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "exec/helper-proto.h"
#include "mmu.h"
-#include "exec/cpu_ldst.h"
#include "exec/translator.h"
#include "crisv32-decode.h"
#include "qemu/qemu-print.h"
@@ -223,37 +221,28 @@ static int sign_extend(unsigned int val, unsigned int width)
}
static int cris_fetch(CPUCRISState *env, DisasContext *dc, uint32_t addr,
- unsigned int size, unsigned int sign)
+ unsigned int size, bool sign)
{
int r;
switch (size) {
case 4:
- {
- r = cpu_ldl_code(env, addr);
+ r = translator_ldl(env, &dc->base, addr);
break;
- }
case 2:
- {
+ r = translator_lduw(env, &dc->base, addr);
if (sign) {
- r = cpu_ldsw_code(env, addr);
- } else {
- r = cpu_lduw_code(env, addr);
+ r = (int16_t)r;
}
break;
- }
case 1:
- {
+ r = translator_ldub(env, &dc->base, addr);
if (sign) {
- r = cpu_ldsb_code(env, addr);
- } else {
- r = cpu_ldub_code(env, addr);
+ r = (int8_t)r;
}
break;
- }
default:
- cpu_abort(CPU(dc->cpu), "Invalid fetch size %d\n", size);
- break;
+ g_assert_not_reached();
}
return r;
}
@@ -2869,7 +2858,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc)
int i;
/* Load a halfword onto the instruction register. */
- dc->ir = cris_fetch(env, dc, dc->pc, 2, 0);
+ dc->ir = cris_fetch(env, dc, dc->pc, 2, 0);
/* Now decode it. */
dc->opcode = EXTRACT_FIELD(dc->ir, 4, 11);
@@ -3148,22 +3137,12 @@ static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void cris_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- if (!DISAS_CRIS) {
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
- }
-}
-
static const TranslatorOps cris_tr_ops = {
.init_disas_context = cris_tr_init_disas_context,
.tb_start = cris_tr_tb_start,
.insn_start = cris_tr_insn_start,
.translate_insn = cris_tr_translate_insn,
.tb_stop = cris_tr_tb_stop,
- .disas_log = cris_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/cris/translate_v10.c.inc b/target/cris/translate_v10.c.inc
index 73fc27c15d..c15ff47505 100644
--- a/target/cris/translate_v10.c.inc
+++ b/target/cris/translate_v10.c.inc
@@ -165,20 +165,7 @@ static int dec10_prep_move_m(CPUCRISState *env, DisasContext *dc,
/* Load [$rs] onto T1. */
if (is_imm) {
- if (memsize != 4) {
- if (s_ext) {
- if (memsize == 1)
- imm = cpu_ldsb_code(env, dc->pc + 2);
- else
- imm = cpu_ldsw_code(env, dc->pc + 2);
- } else {
- if (memsize == 1)
- imm = cpu_ldub_code(env, dc->pc + 2);
- else
- imm = cpu_lduw_code(env, dc->pc + 2);
- }
- } else
- imm = cpu_ldl_code(env, dc->pc + 2);
+ imm = cris_fetch(env, dc, dc->pc + 2, memsize, s_ext);
tcg_gen_movi_tl(dst, imm);
@@ -929,10 +916,11 @@ static int dec10_dip(CPUCRISState *env, DisasContext *dc)
LOG_DIS("dip pc=%x opcode=%d r%d r%d\n",
dc->pc, dc->opcode, dc->src, dc->dst);
if (dc->src == 15) {
- imm = cpu_ldl_code(env, dc->pc + 2);
+ imm = cris_fetch(env, dc, dc->pc + 2, 4, 0);
tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm);
- if (dc->postinc)
+ if (dc->postinc) {
insn_len += 4;
+ }
tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2);
} else {
gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0);
@@ -1095,10 +1083,10 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc)
if (dc->src == 15) {
LOG_DIS("jump.%d %d r%d r%d direct\n", size,
dc->opcode, dc->src, dc->dst);
- imm = cpu_ldl_code(env, dc->pc + 2);
- if (dc->mode == CRISV10_MODE_AUTOINC)
+ imm = cris_fetch(env, dc, dc->pc + 2, size, 0);
+ if (dc->mode == CRISV10_MODE_AUTOINC) {
insn_len += size;
-
+ }
c = tcg_constant_tl(dc->pc + insn_len);
t_gen_mov_preg_TN(dc, dc->dst, c);
dc->jmp_pc = imm;
@@ -1164,7 +1152,7 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc)
case CRISV10_IND_BCC_M:
cris_cc_mask(dc, 0);
- simm = cpu_ldsw_code(env, dc->pc + 2);
+ simm = cris_fetch(env, dc, dc->pc + 2, 2, 1);
simm += 4;
LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm);
@@ -1185,7 +1173,7 @@ static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc)
unsigned int insn_len = 2;
/* Load a halfword onto the instruction register. */
- dc->ir = cpu_lduw_code(env, dc->pc);
+ dc->ir = cris_fetch(env, dc, dc->pc, 2, 0);
/* Now decode it. */
dc->opcode = EXTRACT_FIELD(dc->ir, 6, 9);
diff --git a/target/hexagon/README b/target/hexagon/README
index 746ebec378..7ffd517d70 100644
--- a/target/hexagon/README
+++ b/target/hexagon/README
@@ -43,11 +43,9 @@ target/hexagon/gen_semantics.c. This step produces
That file is consumed by the following python scripts to produce the indicated
header files in <BUILD_DIR>/target/hexagon
gen_opcodes_def.py -> opcodes_def_generated.h.inc
- gen_op_regs.py -> op_regs_generated.h.inc
gen_printinsn.py -> printinsn_generated.h.inc
gen_op_attribs.py -> op_attribs_generated.h.inc
gen_helper_protos.py -> helper_protos_generated.h.inc
- gen_shortcode.py -> shortcode_generated.h.inc
gen_tcg_funcs.py -> tcg_funcs_generated.c.inc
gen_tcg_func_table.py -> tcg_func_table_generated.c.inc
gen_helper_funcs.py -> helper_funcs_generated.c.inc
@@ -183,10 +181,11 @@ when the override is present.
}
We also generate an analyze_<tag> function for each instruction. Currently,
-these functions record the writes to registers by calling ctx_log_*. During
-gen_start_packet, we invoke the analyze_<tag> function for each instruction in
-the packet, and we mark the implicit writes. After the analysis is performed,
-we initialize the result register for each of the predicated assignments.
+these functions record the reads and writes to registers by calling ctx_log_*.
+During gen_start_packet, we invoke the analyze_<tag> function for each instruction in
+the packet, and we mark the implicit writes. The analysis determines if the packet
+semantics can be short-circuited. If not, we initialize the result register for each
+of the predicated assignments.
In addition to instruction semantics, we use a generator to create the decode
tree. This generation is a four step process.
diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc
index 87942d46f4..9e3a05f882 100644
--- a/target/hexagon/attribs_def.h.inc
+++ b/target/hexagon/attribs_def.h.inc
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -117,6 +117,7 @@ DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1 register", "", "")
DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "")
DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "")
DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "")
+DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "")
DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "")
DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "")
DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "")
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index a56bb4b075..64cc05cca7 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -257,7 +257,7 @@ static vaddr hexagon_cpu_get_pc(CPUState *cs)
static void hexagon_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc;
}
diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c
index a40210ca1e..23deba2426 100644
--- a/target/hexagon/decode.c
+++ b/target/hexagon/decode.c
@@ -115,22 +115,13 @@ static void
decode_fill_newvalue_regno(Packet *packet)
{
int i, use_regidx, offset, def_idx, dst_idx;
- uint16_t def_opcode, use_opcode;
- char *dststr;
for (i = 1; i < packet->num_insns; i++) {
if (GET_ATTRIB(packet->insn[i].opcode, A_DOTNEWVALUE) &&
!GET_ATTRIB(packet->insn[i].opcode, A_EXTENSION)) {
- use_opcode = packet->insn[i].opcode;
-
- /* It's a store, so we're adjusting the Nt field */
- if (GET_ATTRIB(use_opcode, A_STORE)) {
- use_regidx = strchr(opcode_reginfo[use_opcode], 't') -
- opcode_reginfo[use_opcode];
- } else { /* It's a Jump, so we're adjusting the Ns field */
- use_regidx = strchr(opcode_reginfo[use_opcode], 's') -
- opcode_reginfo[use_opcode];
- }
+
+ g_assert(packet->insn[i].new_read_idx != -1);
+ use_regidx = packet->insn[i].new_read_idx;
/*
* What's encoded at the N-field is the offset to who's producing
@@ -151,37 +142,9 @@ decode_fill_newvalue_regno(Packet *packet)
*/
g_assert(!((def_idx < 0) || (def_idx > (packet->num_insns - 1))));
- /*
- * packet->insn[def_idx] is the producer
- * Figure out which type of destination it produces
- * and the corresponding index in the reginfo
- */
- def_opcode = packet->insn[def_idx].opcode;
- dststr = strstr(opcode_wregs[def_opcode], "Rd");
- if (dststr) {
- dststr = strchr(opcode_reginfo[def_opcode], 'd');
- } else {
- dststr = strstr(opcode_wregs[def_opcode], "Rx");
- if (dststr) {
- dststr = strchr(opcode_reginfo[def_opcode], 'x');
- } else {
- dststr = strstr(opcode_wregs[def_opcode], "Re");
- if (dststr) {
- dststr = strchr(opcode_reginfo[def_opcode], 'e');
- } else {
- dststr = strstr(opcode_wregs[def_opcode], "Ry");
- if (dststr) {
- dststr = strchr(opcode_reginfo[def_opcode], 'y');
- } else {
- g_assert_not_reached();
- }
- }
- }
- }
- g_assert(dststr != NULL);
-
/* Now patch up the consumer with the register number */
- dst_idx = dststr - opcode_reginfo[def_opcode];
+ g_assert(packet->insn[def_idx].dest_idx != -1);
+ dst_idx = packet->insn[def_idx].dest_idx;
packet->insn[i].regno[use_regidx] =
packet->insn[def_idx].regno[dst_idx];
/*
@@ -362,8 +325,7 @@ static void decode_shuffle_for_execution(Packet *packet)
for (flag = false, i = 0; i < last_insn + 1; i++) {
int opcode = packet->insn[i].opcode;
- if ((strstr(opcode_wregs[opcode], "Pd4") ||
- strstr(opcode_wregs[opcode], "Pe4")) &&
+ if (packet->insn[i].has_pred_dest &&
GET_ATTRIB(opcode, A_STORE) == 0) {
/* This should be a compare (not a store conditional) */
if (flag) {
diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py
index a9af666cef..54bac19724 100755
--- a/target/hexagon/gen_analyze_funcs.py
+++ b/target/hexagon/gen_analyze_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -43,59 +43,53 @@ def gen_analyze_func(f, tag, regs, imms):
f.write("{\n")
f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n")
-
- i = 0
- ## Analyze all the registers
- for regtype, regid in regs:
- reg = hex_common.get_register(tag, regtype, regid)
- if reg.is_written():
- reg.analyze_write(f, tag, i)
+ if (hex_common.is_hvx_insn(tag)):
+ if hex_common.has_hvx_helper(tag):
+ f.write(
+ " const bool G_GNUC_UNUSED insn_has_hvx_helper = true;\n"
+ )
+ f.write(" ctx_start_hvx_insn(ctx);\n")
else:
- reg.analyze_read(f, i)
- i += 1
-
- has_generated_helper = not hex_common.skip_qemu_helper(
- tag
- ) and not hex_common.is_idef_parser_enabled(tag)
-
- ## Mark HVX instructions with generated helpers
- if (has_generated_helper and
- "A_CVI" in hex_common.attribdict[tag]):
- f.write(" ctx->has_hvx_helper = true;\n")
+ f.write(
+ " const bool G_GNUC_UNUSED insn_has_hvx_helper = false;\n"
+ )
+
+ ## Declare all the registers
+ for regno, register in enumerate(regs):
+ reg_type, reg_id = register
+ reg = hex_common.get_register(tag, reg_type, reg_id)
+ reg.decl_reg_num(f, regno)
+
+ ## Analyze the register reads
+ for regno, register in enumerate(regs):
+ reg_type, reg_id = register
+ reg = hex_common.get_register(tag, reg_type, reg_id)
+ if reg.is_read():
+ reg.analyze_read(f, regno)
+
+ ## Analyze the register writes
+ for regno, register in enumerate(regs):
+ reg_type, reg_id = register
+ reg = hex_common.get_register(tag, reg_type, reg_id)
+ if reg.is_written():
+ reg.analyze_write(f, tag, regno)
f.write("}\n\n")
def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.read_overrides_file(sys.argv[3])
- hex_common.read_overrides_file(sys.argv[4])
- ## Whether or not idef-parser is enabled is
- ## determined by the number of arguments to
- ## this script:
- ##
- ## 5 args. -> not enabled,
- ## 6 args. -> idef-parser enabled.
- ##
- ## The 6:th arg. then holds a list of the successfully
- ## parsed instructions.
- is_idef_parser_enabled = len(sys.argv) > 6
- if is_idef_parser_enabled:
- hex_common.read_idef_parser_enabled_file(sys.argv[5])
- hex_common.calculate_attribs()
- hex_common.init_registers()
+ hex_common.read_common_files()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
with open(sys.argv[-1], "w") as f:
- f.write("#ifndef HEXAGON_TCG_FUNCS_H\n")
- f.write("#define HEXAGON_TCG_FUNCS_H\n\n")
+ f.write("#ifndef HEXAGON_ANALYZE_FUNCS_C_INC\n")
+ f.write("#define HEXAGON_ANALYZE_FUNCS_C_INC\n\n")
for tag in hex_common.tags:
gen_analyze_func(f, tag, tagregs[tag], tagimms[tag])
- f.write("#endif /* HEXAGON_TCG_FUNCS_H */\n")
+ f.write("#endif /* HEXAGON_ANALYZE_FUNCS_C_INC */\n")
if __name__ == "__main__":
diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py
index 9cc3d69c49..e9685bff2f 100755
--- a/target/hexagon/gen_helper_funcs.py
+++ b/target/hexagon/gen_helper_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -102,24 +102,7 @@ def gen_helper_function(f, tag, tagregs, tagimms):
def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.read_overrides_file(sys.argv[3])
- hex_common.read_overrides_file(sys.argv[4])
- ## Whether or not idef-parser is enabled is
- ## determined by the number of arguments to
- ## this script:
- ##
- ## 5 args. -> not enabled,
- ## 6 args. -> idef-parser enabled.
- ##
- ## The 6:th arg. then holds a list of the successfully
- ## parsed instructions.
- is_idef_parser_enabled = len(sys.argv) > 6
- if is_idef_parser_enabled:
- hex_common.read_idef_parser_enabled_file(sys.argv[5])
- hex_common.calculate_attribs()
- hex_common.init_registers()
+ hex_common.read_common_files()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py
index c82b0f54e4..fd2bfd0f36 100755
--- a/target/hexagon/gen_helper_protos.py
+++ b/target/hexagon/gen_helper_protos.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -40,28 +40,19 @@ def gen_helper_prototype(f, tag, tagregs, tagimms):
declared.append(arg.proto_arg)
arguments = ", ".join(declared)
- f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n")
+
+ ## Add the TCG_CALL_NO_RWG_SE flag to helpers that don't take the env
+ ## argument and aren't HVX instructions. Since HVX instructions take
+ ## pointers to their arguments, they will have side effects.
+ if hex_common.need_env(tag) or hex_common.is_hvx_insn(tag):
+ f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n")
+ else:
+ f.write(f"DEF_HELPER_FLAGS_{len(declared) - 1}({tag}, "
+ f"TCG_CALL_NO_RWG_SE, {arguments})\n")
def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.read_overrides_file(sys.argv[3])
- hex_common.read_overrides_file(sys.argv[4])
- ## Whether or not idef-parser is enabled is
- ## determined by the number of arguments to
- ## this script:
- ##
- ## 5 args. -> not enabled,
- ## 6 args. -> idef-parser enabled.
- ##
- ## The 6:th arg. then holds a list of the successfully
- ## parsed instructions.
- is_idef_parser_enabled = len(sys.argv) > 6
- if is_idef_parser_enabled:
- hex_common.read_idef_parser_enabled_file(sys.argv[5])
- hex_common.calculate_attribs()
- hex_common.init_registers()
+ hex_common.read_common_files()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py
index 550a48cb7b..eb494abba8 100644
--- a/target/hexagon/gen_idef_parser_funcs.py
+++ b/target/hexagon/gen_idef_parser_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
+## Copyright(c) 2019-2024 rev.ng Labs Srl. All Rights Reserved.
##
## 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
@@ -44,13 +44,12 @@ import hex_common
##
def main():
hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
hex_common.calculate_attribs()
hex_common.init_registers()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
- with open(sys.argv[3], "w") as f:
+ with open(sys.argv[-1], "w") as f:
f.write('#include "macros.inc"\n\n')
for tag in hex_common.tags:
diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py
index 41074b8573..99448220da 100755
--- a/target/hexagon/gen_op_attribs.py
+++ b/target/hexagon/gen_op_attribs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -25,13 +25,12 @@ import hex_common
def main():
hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
hex_common.calculate_attribs()
##
## Generate all the attributes associated with each instruction
##
- with open(sys.argv[3], "w") as f:
+ with open(sys.argv[-1], "w") as f:
for tag in hex_common.tags:
f.write(
f"OP_ATTRIB({tag},ATTRIBS("
diff --git a/target/hexagon/gen_op_regs.py b/target/hexagon/gen_op_regs.py
deleted file mode 100755
index 7b7b33895a..0000000000
--- a/target/hexagon/gen_op_regs.py
+++ /dev/null
@@ -1,125 +0,0 @@
-#!/usr/bin/env python3
-
-##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
-##
-## 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/>.
-##
-
-import sys
-import re
-import string
-import hex_common
-
-
-##
-## Generate the register and immediate operands for each instruction
-##
-def calculate_regid_reg(tag):
- def letter_inc(x):
- return chr(ord(x) + 1)
-
- ordered_implregs = ["SP", "FP", "LR"]
- srcdst_lett = "X"
- src_lett = "S"
- dst_lett = "D"
- retstr = ""
- mapdict = {}
- for reg in ordered_implregs:
- reg_rd = 0
- reg_wr = 0
- if ("A_IMPLICIT_WRITES_" + reg) in hex_common.attribdict[tag]:
- reg_wr = 1
- if reg_rd and reg_wr:
- retstr += srcdst_lett
- mapdict[srcdst_lett] = reg
- srcdst_lett = letter_inc(srcdst_lett)
- elif reg_rd:
- retstr += src_lett
- mapdict[src_lett] = reg
- src_lett = letter_inc(src_lett)
- elif reg_wr:
- retstr += dst_lett
- mapdict[dst_lett] = reg
- dst_lett = letter_inc(dst_lett)
- return retstr, mapdict
-
-
-def calculate_regid_letters(tag):
- retstr, mapdict = calculate_regid_reg(tag)
- return retstr
-
-
-def strip_reg_prefix(x):
- y = x.replace("UREG.", "")
- y = y.replace("MREG.", "")
- return y.replace("GREG.", "")
-
-
-def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.init_registers()
- tagregs = hex_common.get_tagregs(full=True)
- tagimms = hex_common.get_tagimms()
-
- with open(sys.argv[3], "w") as f:
- for tag in hex_common.tags:
- regs = tagregs[tag]
- rregs = []
- wregs = []
- regids = ""
- for regtype, regid, _, numregs in regs:
- reg = hex_common.get_register(tag, regtype, regid)
- if reg.is_read():
- if regid[0] not in regids:
- regids += regid[0]
- rregs.append(regtype + regid + numregs)
- if reg.is_written():
- wregs.append(regtype + regid + numregs)
- if regid[0] not in regids:
- regids += regid[0]
- for attrib in hex_common.attribdict[tag]:
- if hex_common.attribinfo[attrib]["rreg"]:
- rregs.append(strip_reg_prefix(attribinfo[attrib]["rreg"]))
- if hex_common.attribinfo[attrib]["wreg"]:
- wregs.append(strip_reg_prefix(attribinfo[attrib]["wreg"]))
- regids += calculate_regid_letters(tag)
- f.write(
- f'REGINFO({tag},"{regids}",\t/*RD:*/\t"{",".join(rregs)}",'
- f'\t/*WR:*/\t"{",".join(wregs)}")\n'
- )
-
- for tag in hex_common.tags:
- imms = tagimms[tag]
- f.write(f"IMMINFO({tag}")
- if not imms:
- f.write(""",'u',0,0,'U',0,0""")
- for sign, size, shamt in imms:
- if sign == "r":
- sign = "s"
- if not shamt:
- shamt = "0"
- f.write(f""",'{sign}',{size},{shamt}""")
- if len(imms) == 1:
- if sign.isupper():
- myu = "u"
- else:
- myu = "U"
- f.write(f""",'{myu}',0,0""")
- f.write(")\n")
-
-
-if __name__ == "__main__":
- main()
diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py
index cddd868fe3..536f0eb68a 100755
--- a/target/hexagon/gen_opcodes_def.py
+++ b/target/hexagon/gen_opcodes_def.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -29,7 +29,7 @@ def main():
##
## Generate a list of all the opcodes
##
- with open(sys.argv[3], "w") as f:
+ with open(sys.argv[-1], "w") as f:
for tag in hex_common.tags:
f.write(f"OPCODE({tag}),\n")
diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py
index e570bd7c6a..8bf4d0985c 100755
--- a/target/hexagon/gen_printinsn.py
+++ b/target/hexagon/gen_printinsn.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -97,11 +97,10 @@ def spacify(s):
def main():
hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
immext_casere = re.compile(r"IMMEXT\(([A-Za-z])")
- with open(sys.argv[3], "w") as f:
+ with open(sys.argv[-1], "w") as f:
for tag in hex_common.tags:
if not hex_common.behdict[tag]:
continue
diff --git a/target/hexagon/gen_shortcode.py b/target/hexagon/gen_shortcode.py
deleted file mode 100755
index deb94446c4..0000000000
--- a/target/hexagon/gen_shortcode.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-
-##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
-##
-## 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/>.
-##
-
-import sys
-import re
-import string
-import hex_common
-
-
-def gen_shortcode(f, tag):
- f.write(f"DEF_SHORTCODE({tag}, {hex_common.semdict[tag]})\n")
-
-
-def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.calculate_attribs()
- tagregs = hex_common.get_tagregs()
- tagimms = hex_common.get_tagimms()
-
- with open(sys.argv[3], "w") as f:
- f.write("#ifndef DEF_SHORTCODE\n")
- f.write("#define DEF_SHORTCODE(TAG,SHORTCODE) /* Nothing */\n")
- f.write("#endif\n")
-
- for tag in hex_common.tags:
- ## Skip the priv instructions
- if "A_PRIV" in hex_common.attribdict[tag]:
- continue
- ## Skip the guest instructions
- if "A_GUEST" in hex_common.attribdict[tag]:
- continue
- ## Skip the diag instructions
- if tag == "Y6_diag":
- continue
- if tag == "Y6_diag0":
- continue
- if tag == "Y6_diag1":
- continue
-
- gen_shortcode(f, tag)
-
- f.write("#undef DEF_SHORTCODE\n")
-
-
-if __name__ == "__main__":
- main()
diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h
index 1c4391b415..3fc1f4e281 100644
--- a/target/hexagon/gen_tcg.h
+++ b/target/hexagon/gen_tcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -1369,3 +1369,6 @@
gen_helper_raise_exception(tcg_env, excp); \
} while (0)
#endif
+
+#define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0)
+#define fGEN_TCG_SA1_setin1(SHORTCODE) tcg_gen_movi_tl(RdV, -1)
diff --git a/target/hexagon/gen_tcg_func_table.py b/target/hexagon/gen_tcg_func_table.py
index f998ef0992..978ac1819b 100755
--- a/target/hexagon/gen_tcg_func_table.py
+++ b/target/hexagon/gen_tcg_func_table.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -25,12 +25,11 @@ import hex_common
def main():
hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
hex_common.calculate_attribs()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
- with open(sys.argv[3], "w") as f:
+ with open(sys.argv[-1], "w") as f:
f.write("#ifndef HEXAGON_FUNC_TABLE_H\n")
f.write("#define HEXAGON_FUNC_TABLE_H\n\n")
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py
index 3d8e3cb6a2..05aa0a7855 100755
--- a/target/hexagon/gen_tcg_funcs.py
+++ b/target/hexagon/gen_tcg_funcs.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -108,24 +108,7 @@ def gen_def_tcg_func(f, tag, tagregs, tagimms):
def main():
- hex_common.read_semantics_file(sys.argv[1])
- hex_common.read_attribs_file(sys.argv[2])
- hex_common.read_overrides_file(sys.argv[3])
- hex_common.read_overrides_file(sys.argv[4])
- hex_common.calculate_attribs()
- hex_common.init_registers()
- ## Whether or not idef-parser is enabled is
- ## determined by the number of arguments to
- ## this script:
- ##
- ## 5 args. -> not enabled,
- ## 6 args. -> idef-parser enabled.
- ##
- ## The 6:th arg. then holds a list of the successfully
- ## parsed instructions.
- is_idef_parser_enabled = len(sys.argv) > 6
- if is_idef_parser_enabled:
- hex_common.read_idef_parser_enabled_file(sys.argv[5])
+ is_idef_parser_enabled = hex_common.read_common_files()
tagregs = hex_common.get_tagregs()
tagimms = hex_common.get_tagimms()
diff --git a/target/hexagon/gen_trans_funcs.py b/target/hexagon/gen_trans_funcs.py
index 53e844a44b..9f86b4edbd 100755
--- a/target/hexagon/gen_trans_funcs.py
+++ b/target/hexagon/gen_trans_funcs.py
@@ -68,6 +68,9 @@ def mark_which_imm_extended(f, tag):
## insn->regno[0] = args->Rd;
## insn->regno[1] = args->Rs;
## insn->regno[2] = args->Rt;
+## insn->new_read_idx = -1;
+## insn->dest_idx = 0;
+## insn->has_pred_dest = false;
## return true;
## }
##
@@ -84,14 +87,21 @@ def gen_trans_funcs(f):
insn->opcode = {tag};
"""))
- regno = 0
- for reg in regs:
- reg_type = reg[0]
- reg_id = reg[1]
+ new_read_idx = -1
+ dest_idx = -1
+ has_pred_dest = "false"
+ for regno, (reg_type, reg_id, *_) in enumerate(regs):
+ reg = hex_common.get_register(tag, reg_type, reg_id)
f.write(code_fmt(f"""\
insn->regno[{regno}] = args->{reg_type}{reg_id};
"""))
- regno += 1
+ if reg.is_read() and reg.is_new():
+ new_read_idx = regno
+ # dest_idx should be the first destination, so check for -1
+ if reg.is_written() and dest_idx == -1:
+ dest_idx = regno
+ if reg_type == "P" and reg.is_written() and not reg.is_read():
+ has_pred_dest = "true"
if len(imms) != 0:
mark_which_imm_extended(f, tag)
@@ -112,6 +122,11 @@ def gen_trans_funcs(f):
insn->immed[{immno}] = args->{imm_type}{imm_letter};
"""))
+ f.write(code_fmt(f"""\
+ insn->new_read_idx = {new_read_idx};
+ insn->dest_idx = {dest_idx};
+ insn->has_pred_dest = {has_pred_dest};
+ """))
f.write(textwrap.dedent(f"""\
return true;
{close_curly}
@@ -120,5 +135,6 @@ def gen_trans_funcs(f):
if __name__ == "__main__":
hex_common.read_semantics_file(sys.argv[1])
+ hex_common.init_registers()
with open(sys.argv[2], "w") as f:
gen_trans_funcs(f)
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
index 195620c7ec..15ed4980e4 100755
--- a/target/hexagon/hex_common.py
+++ b/target/hexagon/hex_common.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
##
-## Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -26,7 +26,6 @@ behdict = {} # tag ->behavior
semdict = {} # tag -> semantics
attribdict = {} # tag -> attributes
macros = {} # macro -> macro information...
-attribinfo = {} # Register information and misc
registers = {} # register -> register functions
new_registers = {}
tags = [] # list of all tags
@@ -101,6 +100,7 @@ def calculate_attribs():
add_qemu_macro_attrib('fLSBNEW1', 'A_IMPLICIT_READS_P1')
add_qemu_macro_attrib('fLSBNEW1NOT', 'A_IMPLICIT_READS_P1')
add_qemu_macro_attrib('fREAD_P3', 'A_IMPLICIT_READS_P3')
+ add_qemu_macro_attrib('fREAD_SP', 'A_IMPLICIT_READS_SP')
# Recurse down macros, find attributes from sub-macros
macroValues = list(macros.values())
@@ -197,6 +197,26 @@ def get_tagimms():
return dict(zip(tags, list(map(compute_tag_immediates, tags))))
+def need_p0(tag):
+ return "A_IMPLICIT_READS_P0" in attribdict[tag]
+
+
+def need_sp(tag):
+ return "A_IMPLICIT_READS_SP" in attribdict[tag]
+
+
+def is_hvx_insn(tag):
+ return "A_CVI" in attribdict[tag]
+
+
+def need_env(tag):
+ return ("A_STORE" in attribdict[tag] or
+ "A_LOAD" in attribdict[tag] or
+ "A_CVI_GATHER" in attribdict[tag] or
+ "A_CVI_SCATTER" in attribdict[tag] or
+ "A_IMPLICIT_WRITES_USR" in attribdict[tag])
+
+
def need_slot(tag):
if (
"A_CVI_SCATTER" not in attribdict[tag]
@@ -241,6 +261,16 @@ def is_idef_parser_enabled(tag):
return tag in idef_parser_enabled
+def is_hvx_insn(tag):
+ return "A_CVI" in attribdict[tag]
+
+
+def has_hvx_helper(tag):
+ return (is_hvx_insn(tag) and
+ not skip_qemu_helper(tag) and
+ not is_idef_parser_enabled(tag))
+
+
def imm_name(immlett):
return f"{immlett}iV"
@@ -257,19 +287,6 @@ def read_semantics_file(name):
eval_line = ""
-def read_attribs_file(name):
- attribre = re.compile(
- r"DEF_ATTRIB\(([A-Za-z0-9_]+), ([^,]*), "
- + r'"([A-Za-z0-9_\.]*)", "([A-Za-z0-9_\.]*)"\)'
- )
- for line in open(name, "rt").readlines():
- if not attribre.match(line):
- continue
- (attrib_base, descr, rreg, wreg) = attribre.findall(line)[0]
- attrib_base = "A_" + attrib_base
- attribinfo[attrib_base] = {"rreg": rreg, "wreg": wreg, "descr": descr}
-
-
def read_overrides_file(name):
overridere = re.compile(r"#define fGEN_TCG_([A-Za-z0-9_]+)\(.*")
for line in open(name, "rt").readlines():
@@ -397,10 +414,18 @@ class Source:
class OldSource(Source):
def reg_tcg(self):
return f"{self.regtype}{self.regid}V"
+ def is_old(self):
+ return True
+ def is_new(self):
+ return False
class NewSource(Source):
def reg_tcg(self):
return f"{self.regtype}{self.regid}N"
+ def is_old(self):
+ return False
+ def is_new(self):
+ return True
class ReadWrite:
def reg_tcg(self):
@@ -413,6 +438,10 @@ class ReadWrite:
return True
def is_readwrite(self):
return True
+ def is_old(self):
+ return True
+ def is_new(self):
+ return False
class GprDest(Register, Single, Dest):
def decl_tcg(self, f, tag, regno):
@@ -425,7 +454,6 @@ class GprDest(Register, Single, Dest):
gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write(ctx, {self.reg_num}, {predicated});
@@ -438,7 +466,6 @@ class GprSource(Register, Single, OldSource):
TCGv {self.reg_tcg()} = hex_gpr[{self.reg_num}];
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_reg_read(ctx, {self.reg_num});
"""))
@@ -449,9 +476,8 @@ class GprNewSource(Register, Single, NewSource):
TCGv {self.reg_tcg()} = get_result_gpr(ctx, insn->regno[{regno}]);
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_reg_read(ctx, {self.reg_num});
+ ctx_log_reg_read_new(ctx, {self.reg_num});
"""))
class GprReadWrite(Register, Single, ReadWrite):
@@ -471,8 +497,11 @@ class GprReadWrite(Register, Single, ReadWrite):
f.write(code_fmt(f"""\
gen_log_reg_write(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_reg_read(ctx, {self.reg_num});
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write(ctx, {self.reg_num}, {predicated});
@@ -493,7 +522,6 @@ class ControlDest(Register, Single, Dest):
gen_write_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write(ctx, {self.reg_num}, {predicated});
@@ -511,7 +539,6 @@ class ControlSource(Register, Single, OldSource):
gen_read_ctrl_reg(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_reg_read(ctx, {self.reg_num});
"""))
@@ -532,7 +559,6 @@ class ModifierSource(Register, Single, OldSource):
declared.append(self.reg_tcg())
declared.append("CS")
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_reg_read(ctx, {self.reg_num});
"""))
@@ -548,7 +574,6 @@ class PredDest(Register, Single, Dest):
gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_pred_write(ctx, {self.reg_num});
"""))
@@ -560,7 +585,6 @@ class PredSource(Register, Single, OldSource):
TCGv {self.reg_tcg()} = hex_pred[{self.reg_num}];
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_pred_read(ctx, {self.reg_num});
"""))
@@ -571,9 +595,8 @@ class PredNewSource(Register, Single, NewSource):
TCGv {self.reg_tcg()} = get_result_pred(ctx, insn->regno[{regno}]);
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_pred_read(ctx, {self.reg_num});
+ ctx_log_pred_read_new(ctx, {self.reg_num});
"""))
class PredReadWrite(Register, Single, ReadWrite):
@@ -587,8 +610,11 @@ class PredReadWrite(Register, Single, ReadWrite):
f.write(code_fmt(f"""\
gen_log_pred_write(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_pred_read(ctx, {self.reg_num});
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_pred_write(ctx, {self.reg_num});
"""))
@@ -605,7 +631,6 @@ class PairDest(Register, Pair, Dest):
gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated});
@@ -621,7 +646,6 @@ class PairSource(Register, Pair, OldSource):
hex_gpr[{self.reg_num} + 1]);
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_reg_read_pair(ctx, {self.reg_num});
"""))
@@ -640,8 +664,11 @@ class PairReadWrite(Register, Pair, ReadWrite):
f.write(code_fmt(f"""\
gen_log_reg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_reg_read_pair(ctx, {self.reg_num});
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated});
@@ -663,7 +690,6 @@ class ControlPairDest(Register, Pair, Dest):
gen_write_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
ctx_log_reg_write_pair(ctx, {self.reg_num}, {predicated});
@@ -681,7 +707,6 @@ class ControlPairSource(Register, Pair, OldSource):
gen_read_ctrl_reg_pair(ctx, {self.reg_num}, {self.reg_tcg()});
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
ctx_log_reg_read_pair(ctx, {self.reg_num});
"""))
@@ -705,11 +730,11 @@ class VRegDest(Register, Hvx, Dest):
/* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
newv = hvx_newv(tag)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
- ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated});
+ ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated},
+ insn_has_hvx_helper);
"""))
class VRegSource(Register, Hvx, OldSource):
@@ -728,9 +753,8 @@ class VRegSource(Register, Hvx, OldSource):
/* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_vreg_read(ctx, {self.reg_num});
+ ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
class VRegNewSource(Register, Hvx, NewSource):
@@ -746,9 +770,8 @@ class VRegNewSource(Register, Hvx, NewSource):
/* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_vreg_read(ctx, {self.reg_num});
+ ctx_log_vreg_read_new(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
class VRegReadWrite(Register, Hvx, ReadWrite):
@@ -772,12 +795,16 @@ class VRegReadWrite(Register, Hvx, ReadWrite):
f.write(code_fmt(f"""\
/* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper);
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
newv = hvx_newv(tag)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
- ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated});
+ ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated},
+ insn_has_hvx_helper);
"""))
class VRegTmp(Register, Hvx, ReadWrite):
@@ -803,12 +830,16 @@ class VRegTmp(Register, Hvx, ReadWrite):
f.write(code_fmt(f"""\
/* {self.reg_tcg()} is *(MMVector *)({self.helper_arg_name()}) */
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper);
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
newv = hvx_newv(tag)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
- ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated});
+ ctx_log_vreg_write(ctx, {self.reg_num}, {newv}, {predicated},
+ insn_has_hvx_helper);
"""))
class VRegPairDest(Register, Hvx, Dest):
@@ -830,11 +861,11 @@ class VRegPairDest(Register, Hvx, Dest):
/* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
newv = hvx_newv(tag)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
- ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated});
+ ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated},
+ insn_has_hvx_helper);
"""))
class VRegPairSource(Register, Hvx, OldSource):
@@ -860,9 +891,8 @@ class VRegPairSource(Register, Hvx, OldSource):
/* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_vreg_read_pair(ctx, {self.reg_num});
+ ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
class VRegPairReadWrite(Register, Hvx, ReadWrite):
@@ -892,12 +922,16 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite):
f.write(code_fmt(f"""\
/* {self.reg_tcg()} is *(MMVectorPair *)({self.helper_arg_name()}) */
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper);
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
newv = hvx_newv(tag)
predicated = "true" if is_predicated(tag) else "false"
f.write(code_fmt(f"""\
- ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated});
+ ctx_log_vreg_write_pair(ctx, {self.reg_num}, {newv}, {predicated},
+ insn_has_hvx_helper);
"""))
class QRegDest(Register, Hvx, Dest):
@@ -919,9 +953,8 @@ class QRegDest(Register, Hvx, Dest):
/* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */
"""))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_qreg_write(ctx, {self.reg_num});
+ ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
class QRegSource(Register, Hvx, OldSource):
@@ -941,9 +974,8 @@ class QRegSource(Register, Hvx, OldSource):
/* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */
"""))
def analyze_read(self, f, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_qreg_read(ctx, {self.reg_num});
+ ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
class QRegReadWrite(Register, Hvx, ReadWrite):
@@ -967,10 +999,13 @@ class QRegReadWrite(Register, Hvx, ReadWrite):
f.write(code_fmt(f"""\
/* {self.reg_tcg()} is *(MMQReg *)({self.helper_arg_name()}) */
"""))
+ def analyze_read(self, f, regno):
+ f.write(code_fmt(f"""\
+ ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper);
+ """))
def analyze_write(self, f, tag, regno):
- self.decl_reg_num(f, regno)
f.write(code_fmt(f"""\
- ctx_log_qreg_write(ctx, {self.reg_num});
+ ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper);
"""))
def init_registers():
@@ -1060,11 +1095,12 @@ def helper_args(tag, regs, imms):
args = []
## First argument is the CPU state
- args.append(HelperArg(
- "env",
- "tcg_env",
- "CPUHexagonState *env"
- ))
+ if need_env(tag):
+ args.append(HelperArg(
+ "env",
+ "tcg_env",
+ "CPUHexagonState *env"
+ ))
## For predicated instructions, we pass in the destination register
if is_predicated(tag):
@@ -1118,6 +1154,18 @@ def helper_args(tag, regs, imms):
"tcg_constant_tl(ctx->next_PC)",
"target_ulong next_PC"
))
+ if need_p0(tag):
+ args.append(HelperArg(
+ "i32",
+ "hex_pred[0]",
+ "uint32_t P0"
+ ))
+ if need_sp(tag):
+ args.append(HelperArg(
+ "i32",
+ "hex_gpr[HEX_REG_SP]",
+ "uint32_t SP"
+ ))
if need_slot(tag):
args.append(HelperArg(
"i32",
@@ -1131,3 +1179,24 @@ def helper_args(tag, regs, imms):
"uint32_t part1"
))
return args
+
+
+def read_common_files():
+ read_semantics_file(sys.argv[1])
+ read_overrides_file(sys.argv[2])
+ read_overrides_file(sys.argv[3])
+ ## Whether or not idef-parser is enabled is
+ ## determined by the number of arguments to
+ ## this script:
+ ##
+ ## 4 args. -> not enabled,
+ ## 5 args. -> idef-parser enabled.
+ ##
+ ## The 5:th arg. then holds a list of the successfully
+ ## parsed instructions.
+ is_idef_parser_enabled = len(sys.argv) > 5
+ if is_idef_parser_enabled:
+ read_idef_parser_enabled_file(sys.argv[4])
+ calculate_attribs()
+ init_registers()
+ return is_idef_parser_enabled
diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h
index 3e7a22c91e..24dcf7fe9f 100644
--- a/target/hexagon/insn.h
+++ b/target/hexagon/insn.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -39,6 +39,9 @@ struct Instruction {
uint32_t slot:3;
uint32_t which_extended:1; /* If has an extender, which immediate */
uint32_t new_value_producer_slot:4;
+ int32_t new_read_idx;
+ int32_t dest_idx;
+ bool has_pred_dest;
bool part1; /*
* cmp-jumps are split into two insns.
diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
index 1376d6ccc1..feb798c6c0 100644
--- a/target/hexagon/macros.h
+++ b/target/hexagon/macros.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -343,7 +343,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift)
#define fREAD_LR() (env->gpr[HEX_REG_LR])
-#define fREAD_SP() (env->gpr[HEX_REG_SP])
+#define fREAD_SP() (SP)
#define fREAD_LC0 (env->gpr[HEX_REG_LC0])
#define fREAD_LC1 (env->gpr[HEX_REG_LC1])
#define fREAD_SA0 (env->gpr[HEX_REG_SA0])
@@ -358,7 +358,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift)
#endif
#define fREAD_PC() (PC)
-#define fREAD_P0() (env->pred[0])
+#define fREAD_P0() (P0)
#define fCHECK_PCALIGN(A)
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build
index fb480afc03..b0b253aa6b 100644
--- a/target/hexagon/meson.build
+++ b/target/hexagon/meson.build
@@ -1,5 +1,5 @@
##
-## Copyright(c) 2020-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+## Copyright(c) 2020-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
##
## 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
@@ -18,7 +18,6 @@
hexagon_ss = ss.source_set()
hex_common_py = 'hex_common.py'
-attribs_def = meson.current_source_dir() / 'attribs_def.h.inc'
gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h'
gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h'
idef_parser_dir = meson.current_source_dir() / 'idef-parser'
@@ -42,28 +41,17 @@ hexagon_ss.add(semantics_generated)
#
# Step 2
# We use Python scripts to generate the following files
-# shortcode_generated.h.inc
# tcg_func_table_generated.c.inc
# printinsn_generated.h.inc
-# op_regs_generated.h.inc
# op_attribs_generated.h.inc
# opcodes_def_generated.h.inc
#
-shortcode_generated = custom_target(
- 'shortcode_generated.h.inc',
- output: 'shortcode_generated.h.inc',
- depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_shortcode.py'), semantics_generated, attribs_def, '@OUTPUT@'],
-)
-hexagon_ss.add(shortcode_generated)
-
tcg_func_table_generated = custom_target(
'tcg_func_table_generated.c.inc',
output: 'tcg_func_table_generated.c.inc',
depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_tcg_func_table.py'), semantics_generated, attribs_def, '@OUTPUT@'],
+ depend_files: [hex_common_py],
+ command: [python, files('gen_tcg_func_table.py'), semantics_generated, '@OUTPUT@'],
)
hexagon_ss.add(tcg_func_table_generated)
@@ -71,26 +59,17 @@ printinsn_generated = custom_target(
'printinsn_generated.h.inc',
output: 'printinsn_generated.h.inc',
depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_printinsn.py'), semantics_generated, attribs_def, '@OUTPUT@'],
+ depend_files: [hex_common_py],
+ command: [python, files('gen_printinsn.py'), semantics_generated, '@OUTPUT@'],
)
hexagon_ss.add(printinsn_generated)
-op_regs_generated = custom_target(
- 'op_regs_generated.h.inc',
- output: 'op_regs_generated.h.inc',
- depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_op_regs.py'), semantics_generated, attribs_def, '@OUTPUT@'],
-)
-hexagon_ss.add(op_regs_generated)
-
op_attribs_generated = custom_target(
'op_attribs_generated.h.inc',
output: 'op_attribs_generated.h.inc',
depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_op_attribs.py'), semantics_generated, attribs_def, '@OUTPUT@'],
+ depend_files: [hex_common_py],
+ command: [python, files('gen_op_attribs.py'), semantics_generated, '@OUTPUT@'],
)
hexagon_ss.add(op_attribs_generated)
@@ -98,8 +77,8 @@ opcodes_def_generated = custom_target(
'opcodes_def_generated.h.inc',
output: 'opcodes_def_generated.h.inc',
depends: [semantics_generated],
- depend_files: [hex_common_py, attribs_def],
- command: [python, files('gen_opcodes_def.py'), semantics_generated, attribs_def, '@OUTPUT@'],
+ depend_files: [hex_common_py],
+ command: [python, files('gen_opcodes_def.py'), semantics_generated, '@OUTPUT@'],
)
hexagon_ss.add(opcodes_def_generated)
@@ -110,7 +89,7 @@ hexagon_ss.add(opcodes_def_generated)
#
gen_dectree_import = executable(
'gen_dectree_import',
- 'gen_dectree_import.c', opcodes_def_generated, op_regs_generated,
+ 'gen_dectree_import.c', opcodes_def_generated,
native: true, build_by_default: false)
iset_py = custom_target(
@@ -298,7 +277,7 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs
output: 'idef_parser_input.h.inc',
depends: [semantics_generated],
depend_files: [hex_common_py],
- command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, attribs_def, '@OUTPUT@'],
+ command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, '@OUTPUT@'],
)
preprocessed_idef_parser_input_generated = custom_target(
@@ -367,12 +346,12 @@ if idef_parser_enabled and 'hexagon-linux-user' in target_dirs
# Setup input and dependencies for the next step, this depends on whether or
# not idef-parser is enabled
helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg]
- helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list]
+ helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, idef_generated_list]
else
# Setup input and dependencies for the next step, this depends on whether or
# not idef-parser is enabled
helper_dep = [semantics_generated]
- helper_in = [semantics_generated, attribs_def, gen_tcg_h, gen_tcg_hvx_h]
+ helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h]
endif
#
@@ -386,7 +365,7 @@ helper_protos_generated = custom_target(
'helper_protos_generated.h.inc',
output: 'helper_protos_generated.h.inc',
depends: helper_dep,
- depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
+ depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h],
command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'],
)
hexagon_ss.add(helper_protos_generated)
@@ -395,7 +374,7 @@ helper_funcs_generated = custom_target(
'helper_funcs_generated.c.inc',
output: 'helper_funcs_generated.c.inc',
depends: helper_dep,
- depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
+ depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h],
command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'],
)
hexagon_ss.add(helper_funcs_generated)
@@ -404,7 +383,7 @@ tcg_funcs_generated = custom_target(
'tcg_funcs_generated.c.inc',
output: 'tcg_funcs_generated.c.inc',
depends: helper_dep,
- depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
+ depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h],
command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'],
)
hexagon_ss.add(tcg_funcs_generated)
@@ -413,7 +392,7 @@ analyze_funcs_generated = custom_target(
'analyze_funcs_generated.c.inc',
output: 'analyze_funcs_generated.c.inc',
depends: helper_dep,
- depend_files: [hex_common_py, attribs_def, gen_tcg_h, gen_tcg_hvx_h],
+ depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h],
command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'],
)
hexagon_ss.add(analyze_funcs_generated)
diff --git a/target/hexagon/mmvec/decode_ext_mmvec.c b/target/hexagon/mmvec/decode_ext_mmvec.c
index 202d84c7c0..f850d0154d 100644
--- a/target/hexagon/mmvec/decode_ext_mmvec.c
+++ b/target/hexagon/mmvec/decode_ext_mmvec.c
@@ -28,19 +28,15 @@ check_new_value(Packet *pkt)
{
/* .new value for a MMVector store */
int i, j;
- const char *reginfo;
- const char *destletters;
- const char *dststr = NULL;
uint16_t def_opcode;
- char letter;
for (i = 1; i < pkt->num_insns; i++) {
uint16_t use_opcode = pkt->insn[i].opcode;
if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) &&
GET_ATTRIB(use_opcode, A_CVI) &&
GET_ATTRIB(use_opcode, A_STORE)) {
- int use_regidx = strchr(opcode_reginfo[use_opcode], 's') -
- opcode_reginfo[use_opcode];
+ int use_regidx = pkt->insn[i].new_read_idx;
+ g_assert(pkt->insn[i].new_read_idx != -1);
/*
* What's encoded at the N-field is the offset to who's producing
* the value.
@@ -68,31 +64,19 @@ check_new_value(Packet *pkt)
/* def_idx is the index of the producer */
def_opcode = pkt->insn[def_idx].opcode;
- reginfo = opcode_reginfo[def_opcode];
- destletters = "dexy";
- for (j = 0; (letter = destletters[j]) != 0; j++) {
- dststr = strchr(reginfo, letter);
- if (dststr != NULL) {
- break;
- }
- }
- if ((dststr == NULL) && GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
+ if ((pkt->insn[def_idx].dest_idx == -1) &&
+ GET_ATTRIB(def_opcode, A_CVI_GATHER)) {
pkt->insn[i].regno[use_regidx] = def_oreg;
pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot;
} else {
- if (dststr == NULL) {
+ if (pkt->insn[def_idx].dest_idx == -1) {
/* still not there, we have a bad packet */
g_assert_not_reached();
}
- int def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
+ int def_regnum =
+ pkt->insn[def_idx].regno[pkt->insn[def_idx].dest_idx];
/* Now patch up the consumer with the register number */
pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg;
- /* special case for (Vx,Vy) */
- dststr = strchr(reginfo, 'y');
- if (def_oreg && strchr(reginfo, 'x') && dststr) {
- def_regnum = pkt->insn[def_idx].regno[dststr - reginfo];
- pkt->insn[i].regno[use_regidx] = def_regnum;
- }
/*
* We need to remember who produces this value to later
* check if it was dynamically cancelled
diff --git a/target/hexagon/opcodes.c b/target/hexagon/opcodes.c
index 1f7f3def38..c8bde2f9e9 100644
--- a/target/hexagon/opcodes.c
+++ b/target/hexagon/opcodes.c
@@ -36,41 +36,6 @@ const char * const opcode_names[] = {
#undef OPCODE
};
-const char * const opcode_reginfo[] = {
-#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */
-#define REGINFO(TAG, REGINFO, RREGS, WREGS) REGINFO,
-#include "op_regs_generated.h.inc"
- NULL
-#undef REGINFO
-#undef IMMINFO
-};
-
-
-const char * const opcode_rregs[] = {
-#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */
-#define REGINFO(TAG, REGINFO, RREGS, WREGS) RREGS,
-#include "op_regs_generated.h.inc"
- NULL
-#undef REGINFO
-#undef IMMINFO
-};
-
-
-const char * const opcode_wregs[] = {
-#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */
-#define REGINFO(TAG, REGINFO, RREGS, WREGS) WREGS,
-#include "op_regs_generated.h.inc"
- NULL
-#undef REGINFO
-#undef IMMINFO
-};
-
-const char * const opcode_short_semantics[] = {
-#define DEF_SHORTCODE(TAG, SHORTCODE) [TAG] = #SHORTCODE,
-#include "shortcode_generated.h.inc"
-#undef DEF_SHORTCODE
- NULL
-};
DECLARE_BITMAP(opcode_attribs[XX_LAST_OPCODE], A_ZZ_LASTATTRIB);
diff --git a/target/hexagon/opcodes.h b/target/hexagon/opcodes.h
index fa7e321950..0ee11bd445 100644
--- a/target/hexagon/opcodes.h
+++ b/target/hexagon/opcodes.h
@@ -40,10 +40,6 @@ typedef enum {
extern const char * const opcode_names[];
-extern const char * const opcode_reginfo[];
-extern const char * const opcode_rregs[];
-extern const char * const opcode_wregs[];
-
typedef struct {
const char * const encoding;
const EncClass enc_class;
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 47a870f42d..4b1bee3c6d 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -380,70 +380,8 @@ static bool need_commit(DisasContext *ctx)
return true;
}
- if (pkt->num_insns == 1) {
- if (pkt->pkt_has_hvx) {
- /*
- * The HVX instructions with generated helpers use
- * pass-by-reference, so they need the read/write overlap
- * check below.
- * The HVX instructions with overrides are OK.
- */
- if (!ctx->has_hvx_helper) {
- return false;
- }
- } else {
- return false;
- }
- }
-
- /* Check for overlap between register reads and writes */
- for (int i = 0; i < ctx->reg_log_idx; i++) {
- int rnum = ctx->reg_log[i];
- if (test_bit(rnum, ctx->regs_read)) {
- return true;
- }
- }
-
- /* Check for overlap between predicate reads and writes */
- for (int i = 0; i < ctx->preg_log_idx; i++) {
- int pnum = ctx->preg_log[i];
- if (test_bit(pnum, ctx->pregs_read)) {
- return true;
- }
- }
-
- /* Check for overlap between HVX reads and writes */
- for (int i = 0; i < ctx->vreg_log_idx; i++) {
- int vnum = ctx->vreg_log[i];
- if (test_bit(vnum, ctx->vregs_read)) {
- return true;
- }
- }
- if (!bitmap_empty(ctx->vregs_updated_tmp, NUM_VREGS)) {
- int i = find_first_bit(ctx->vregs_updated_tmp, NUM_VREGS);
- while (i < NUM_VREGS) {
- if (test_bit(i, ctx->vregs_read)) {
- return true;
- }
- i = find_next_bit(ctx->vregs_updated_tmp, NUM_VREGS, i + 1);
- }
- }
- if (!bitmap_empty(ctx->vregs_select, NUM_VREGS)) {
- int i = find_first_bit(ctx->vregs_select, NUM_VREGS);
- while (i < NUM_VREGS) {
- if (test_bit(i, ctx->vregs_read)) {
- return true;
- }
- i = find_next_bit(ctx->vregs_select, NUM_VREGS, i + 1);
- }
- }
-
- /* Check for overlap between HVX predicate reads and writes */
- for (int i = 0; i < ctx->qreg_log_idx; i++) {
- int qnum = ctx->qreg_log[i];
- if (test_bit(qnum, ctx->qregs_read)) {
- return true;
- }
+ if (ctx->read_after_write || ctx->has_hvx_overlap) {
+ return true;
}
return false;
@@ -467,7 +405,8 @@ static void mark_implicit_pred_reads(DisasContext *ctx)
static void analyze_packet(DisasContext *ctx)
{
Packet *pkt = ctx->pkt;
- ctx->has_hvx_helper = false;
+ ctx->read_after_write = false;
+ ctx->has_hvx_overlap = false;
for (int i = 0; i < pkt->num_insns; i++) {
Insn *insn = &pkt->insn[i];
ctx->insn = insn;
@@ -492,21 +431,19 @@ static void gen_start_packet(DisasContext *ctx)
ctx->next_PC = next_PC;
ctx->reg_log_idx = 0;
bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS);
- bitmap_zero(ctx->regs_read, TOTAL_PER_THREAD_REGS);
bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS);
ctx->preg_log_idx = 0;
bitmap_zero(ctx->pregs_written, NUM_PREGS);
- bitmap_zero(ctx->pregs_read, NUM_PREGS);
ctx->future_vregs_idx = 0;
ctx->tmp_vregs_idx = 0;
ctx->vreg_log_idx = 0;
+ bitmap_zero(ctx->vregs_written, NUM_VREGS);
bitmap_zero(ctx->vregs_updated_tmp, NUM_VREGS);
bitmap_zero(ctx->vregs_updated, NUM_VREGS);
bitmap_zero(ctx->vregs_select, NUM_VREGS);
bitmap_zero(ctx->predicated_future_vregs, NUM_VREGS);
bitmap_zero(ctx->predicated_tmp_vregs, NUM_VREGS);
- bitmap_zero(ctx->vregs_read, NUM_VREGS);
- bitmap_zero(ctx->qregs_read, NUM_QREGS);
+ bitmap_zero(ctx->qregs_written, NUM_QREGS);
ctx->qreg_log_idx = 0;
for (i = 0; i < STORES_MAX; i++) {
ctx->store_width[i] = 0;
@@ -1085,7 +1022,7 @@ static bool pkt_crosses_page(CPUHexagonState *env, DisasContext *ctx)
int nwords;
for (nwords = 0; !found_end && nwords < PACKET_WORDS_MAX; nwords++) {
- uint32_t word = cpu_ldl_code(env,
+ uint32_t word = translator_ldl(env, &ctx->base,
ctx->base.pc_next + nwords * sizeof(uint32_t));
found_end = is_packet_end(word);
}
@@ -1138,21 +1075,12 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void hexagon_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
-
static const TranslatorOps hexagon_tr_ops = {
.init_disas_context = hexagon_tr_init_disas_context,
.tb_start = hexagon_tr_tb_start,
.insn_start = hexagon_tr_insn_start,
.translate_insn = hexagon_tr_translate_packet,
.tb_stop = hexagon_tr_tb_stop,
- .disas_log = hexagon_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index 4dd59c6726..00cc2bcd63 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -38,12 +38,10 @@ typedef struct DisasContext {
int reg_log[REG_WRITES_MAX];
int reg_log_idx;
DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS);
- DECLARE_BITMAP(regs_read, TOTAL_PER_THREAD_REGS);
DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS);
int preg_log[PRED_WRITES_MAX];
int preg_log_idx;
DECLARE_BITMAP(pregs_written, NUM_PREGS);
- DECLARE_BITMAP(pregs_read, NUM_PREGS);
uint8_t store_width[STORES_MAX];
bool s1_store_processed;
int future_vregs_idx;
@@ -52,22 +50,27 @@ typedef struct DisasContext {
int tmp_vregs_num[VECTOR_TEMPS_MAX];
int vreg_log[NUM_VREGS];
int vreg_log_idx;
+ DECLARE_BITMAP(vregs_written, NUM_VREGS);
+ DECLARE_BITMAP(insn_vregs_written, NUM_VREGS);
DECLARE_BITMAP(vregs_updated_tmp, NUM_VREGS);
DECLARE_BITMAP(vregs_updated, NUM_VREGS);
DECLARE_BITMAP(vregs_select, NUM_VREGS);
DECLARE_BITMAP(predicated_future_vregs, NUM_VREGS);
DECLARE_BITMAP(predicated_tmp_vregs, NUM_VREGS);
- DECLARE_BITMAP(vregs_read, NUM_VREGS);
+ DECLARE_BITMAP(insn_vregs_read, NUM_VREGS);
int qreg_log[NUM_QREGS];
int qreg_log_idx;
- DECLARE_BITMAP(qregs_read, NUM_QREGS);
+ DECLARE_BITMAP(qregs_written, NUM_QREGS);
+ DECLARE_BITMAP(insn_qregs_written, NUM_QREGS);
+ DECLARE_BITMAP(insn_qregs_read, NUM_QREGS);
bool pre_commit;
bool need_commit;
TCGCond branch_cond;
target_ulong branch_dest;
bool is_tight_loop;
bool short_circuit;
- bool has_hvx_helper;
+ bool read_after_write;
+ bool has_hvx_overlap;
TCGv new_value[TOTAL_PER_THREAD_REGS];
TCGv new_pred_value[NUM_PREGS];
TCGv pred_written;
@@ -75,6 +78,8 @@ typedef struct DisasContext {
TCGv dczero_addr;
} DisasContext;
+bool is_gather_store_insn(DisasContext *ctx);
+
static inline void ctx_log_pred_write(DisasContext *ctx, int pnum)
{
if (!test_bit(pnum, ctx->pregs_written)) {
@@ -86,7 +91,14 @@ static inline void ctx_log_pred_write(DisasContext *ctx, int pnum)
static inline void ctx_log_pred_read(DisasContext *ctx, int pnum)
{
- set_bit(pnum, ctx->pregs_read);
+ if (test_bit(pnum, ctx->pregs_written)) {
+ ctx->read_after_write = true;
+ }
+}
+
+static inline void ctx_log_pred_read_new(DisasContext *ctx, int pnum)
+{
+ g_assert(test_bit(pnum, ctx->pregs_written));
}
static inline void ctx_log_reg_write(DisasContext *ctx, int rnum,
@@ -117,7 +129,14 @@ static inline void ctx_log_reg_write_pair(DisasContext *ctx, int rnum,
static inline void ctx_log_reg_read(DisasContext *ctx, int rnum)
{
- set_bit(rnum, ctx->regs_read);
+ if (test_bit(rnum, ctx->regs_written)) {
+ ctx->read_after_write = true;
+ }
+}
+
+static inline void ctx_log_reg_read_new(DisasContext *ctx, int rnum)
+{
+ g_assert(test_bit(rnum, ctx->regs_written));
}
static inline void ctx_log_reg_read_pair(DisasContext *ctx, int rnum)
@@ -131,10 +150,25 @@ intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum,
intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum,
int num, bool alloc_ok);
+static inline void ctx_start_hvx_insn(DisasContext *ctx)
+{
+ bitmap_zero(ctx->insn_vregs_written, NUM_VREGS);
+ bitmap_zero(ctx->insn_vregs_read, NUM_VREGS);
+ bitmap_zero(ctx->insn_qregs_written, NUM_QREGS);
+ bitmap_zero(ctx->insn_qregs_read, NUM_QREGS);
+}
+
static inline void ctx_log_vreg_write(DisasContext *ctx,
int rnum, VRegWriteType type,
- bool is_predicated)
+ bool is_predicated, bool has_helper)
{
+ if (has_helper) {
+ set_bit(rnum, ctx->insn_vregs_written);
+ if (test_bit(rnum, ctx->insn_vregs_read)) {
+ ctx->has_hvx_overlap = true;
+ }
+ }
+ set_bit(rnum, ctx->vregs_written);
if (type != EXT_TMP) {
if (!test_bit(rnum, ctx->vregs_updated)) {
ctx->vreg_log[ctx->vreg_log_idx] = rnum;
@@ -160,33 +194,77 @@ static inline void ctx_log_vreg_write(DisasContext *ctx,
static inline void ctx_log_vreg_write_pair(DisasContext *ctx,
int rnum, VRegWriteType type,
- bool is_predicated)
+ bool is_predicated, bool has_helper)
{
- ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated);
- ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated);
+ ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated, has_helper);
+ ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated, has_helper);
}
-static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum)
+static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum,
+ bool has_helper)
{
- set_bit(rnum, ctx->vregs_read);
+ if (has_helper) {
+ set_bit(rnum, ctx->insn_vregs_read);
+ if (test_bit(rnum, ctx->insn_vregs_written)) {
+ ctx->has_hvx_overlap = true;
+ }
+ }
+ if (test_bit(rnum, ctx->vregs_written)) {
+ ctx->read_after_write = true;
+ }
}
-static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum)
+static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum,
+ bool has_helper)
{
- ctx_log_vreg_read(ctx, rnum ^ 0);
- ctx_log_vreg_read(ctx, rnum ^ 1);
+ g_assert(is_gather_store_insn(ctx) ||
+ test_bit(rnum, ctx->vregs_updated) ||
+ test_bit(rnum, ctx->vregs_select) ||
+ test_bit(rnum, ctx->vregs_updated_tmp));
+ if (has_helper) {
+ set_bit(rnum, ctx->insn_vregs_read);
+ if (test_bit(rnum, ctx->insn_vregs_written)) {
+ ctx->has_hvx_overlap = true;
+ }
+ }
+ if (is_gather_store_insn(ctx)) {
+ ctx->read_after_write = true;
+ }
+}
+
+static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum,
+ bool has_helper)
+{
+ ctx_log_vreg_read(ctx, rnum ^ 0, has_helper);
+ ctx_log_vreg_read(ctx, rnum ^ 1, has_helper);
}
static inline void ctx_log_qreg_write(DisasContext *ctx,
- int rnum)
+ int rnum, bool has_helper)
{
+ if (has_helper) {
+ set_bit(rnum, ctx->insn_qregs_written);
+ if (test_bit(rnum, ctx->insn_qregs_read)) {
+ ctx->has_hvx_overlap = true;
+ }
+ }
+ set_bit(rnum, ctx->qregs_written);
ctx->qreg_log[ctx->qreg_log_idx] = rnum;
ctx->qreg_log_idx++;
}
-static inline void ctx_log_qreg_read(DisasContext *ctx, int qnum)
+static inline void ctx_log_qreg_read(DisasContext *ctx,
+ int qnum, bool has_helper)
{
- set_bit(qnum, ctx->qregs_read);
+ if (has_helper) {
+ set_bit(qnum, ctx->insn_qregs_read);
+ if (test_bit(qnum, ctx->insn_qregs_written)) {
+ ctx->has_hvx_overlap = true;
+ }
+ }
+ if (test_bit(qnum, ctx->qregs_written)) {
+ ctx->read_after_write = true;
+ }
}
extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS];
@@ -205,7 +283,6 @@ extern TCGv hex_vstore_addr[VSTORES_MAX];
extern TCGv hex_vstore_size[VSTORES_MAX];
extern TCGv hex_vstore_pending[VSTORES_MAX];
-bool is_gather_store_insn(DisasContext *ctx);
void process_store(DisasContext *ctx, int slot_num);
FIELD(PROBE_PKT_SCALAR_STORE_S0, MMU_IDX, 0, 2)
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 3831cb6db2..f0507874ce 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -32,61 +32,96 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value)
{
HPPACPU *cpu = HPPA_CPU(cs);
+#ifdef CONFIG_USER_ONLY
+ value |= PRIV_USER;
+#endif
cpu->env.iaoq_f = value;
cpu->env.iaoq_b = value + 4;
}
static vaddr hppa_cpu_get_pc(CPUState *cs)
{
- HPPACPU *cpu = HPPA_CPU(cs);
+ CPUHPPAState *env = cpu_env(cs);
- return cpu->env.iaoq_f;
+ return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0),
+ env->iaoq_f & -4);
}
-static void hppa_cpu_synchronize_from_tb(CPUState *cs,
- const TranslationBlock *tb)
+void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
+ uint64_t *pcsbase, uint32_t *pflags)
{
- HPPACPU *cpu = HPPA_CPU(cs);
+ uint32_t flags = 0;
+ uint64_t cs_base = 0;
+
+ /*
+ * TB lookup assumes that PC contains the complete virtual address.
+ * If we leave space+offset separate, we'll get ITLB misses to an
+ * incomplete virtual address. This also means that we must separate
+ * out current cpu privilege from the low bits of IAOQ_F.
+ */
+ *pc = hppa_cpu_get_pc(env_cpu(env));
+ flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT;
+
+ /*
+ * The only really interesting case is if IAQ_Back is on the same page
+ * as IAQ_Front, so that we can use goto_tb between the blocks. In all
+ * other cases, we'll be ending the TranslationBlock with one insn and
+ * not linking between them.
+ */
+ if (env->iasq_f != env->iasq_b) {
+ cs_base |= CS_BASE_DIFFSPACE;
+ } else if ((env->iaoq_f ^ env->iaoq_b) & TARGET_PAGE_MASK) {
+ cs_base |= CS_BASE_DIFFPAGE;
+ } else {
+ cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK;
+ }
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ /* ??? E, T, H, L bits need to be here, when implemented. */
+ flags |= env->psw_n * PSW_N;
+ flags |= env->psw_xb;
+ flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P);
#ifdef CONFIG_USER_ONLY
- cpu->env.iaoq_f = tb->pc;
- cpu->env.iaoq_b = tb->cs_base;
+ flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus;
#else
- /* Recover the IAOQ values from the GVA + PRIV. */
- uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3;
- target_ulong cs_base = tb->cs_base;
- target_ulong iasq_f = cs_base & ~0xffffffffull;
- int32_t diff = cs_base;
-
- cpu->env.iasq_f = iasq_f;
- cpu->env.iaoq_f = (tb->pc & ~iasq_f) + priv;
- if (diff) {
- cpu->env.iaoq_b = cpu->env.iaoq_f + diff;
+ if ((env->sr[4] == env->sr[5])
+ & (env->sr[4] == env->sr[6])
+ & (env->sr[4] == env->sr[7])) {
+ flags |= TB_FLAG_SR_SAME;
}
#endif
+ *pcsbase = cs_base;
+ *pflags = flags;
+}
+
+static void hppa_cpu_synchronize_from_tb(CPUState *cs,
+ const TranslationBlock *tb)
+{
+ HPPACPU *cpu = HPPA_CPU(cs);
+
+ /* IAQ is always up-to-date before goto_tb. */
cpu->env.psw_n = (tb->flags & PSW_N) != 0;
+ cpu->env.psw_xb = tb->flags & (PSW_X | PSW_B);
}
static void hppa_restore_state_to_opc(CPUState *cs,
const TranslationBlock *tb,
const uint64_t *data)
{
- HPPACPU *cpu = HPPA_CPU(cs);
+ CPUHPPAState *env = cpu_env(cs);
- cpu->env.iaoq_f = data[0];
- if (data[1] != (target_ulong)-1) {
- cpu->env.iaoq_b = data[1];
+ env->iaoq_f = (env->iaoq_f & TARGET_PAGE_MASK) | data[0];
+ if (data[1] != INT32_MIN) {
+ env->iaoq_b = env->iaoq_f + data[1];
}
- cpu->env.unwind_breg = data[2];
+ env->unwind_breg = data[2];
/*
* Since we were executing the instruction at IAOQ_F, and took some
* sort of action that provoked the cpu_restore_state, we can infer
* that the instruction was not nullified.
*/
- cpu->env.psw_n = 0;
+ env->psw_n = 0;
}
static bool hppa_cpu_has_work(CPUState *cs)
@@ -152,6 +187,9 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
hppa_ptlbe(&cpu->env);
}
#endif
+
+ /* Use pc-relative instructions always to simplify the translator. */
+ tcg_cflags_set(cs, CF_PCREL);
}
static void hppa_cpu_initfn(Object *obj)
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index fb2e4c4a98..2bcb3b602b 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -24,6 +24,7 @@
#include "exec/cpu-defs.h"
#include "qemu/cpu-float.h"
#include "qemu/interval-tree.h"
+#include "hw/registerfields.h"
#define MMU_ABS_W_IDX 6
#define MMU_ABS_IDX 7
@@ -41,6 +42,9 @@
#define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1)
#define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX)
+#define PRIV_KERNEL 0
+#define PRIV_USER 3
+
#define TARGET_INSN_START_EXTRA_WORDS 2
/* No need to flush MMU_ABS*_IDX */
@@ -152,6 +156,30 @@
#define CR_IPSW 22
#define CR_EIRR 23
+FIELD(FPSR, ENA_I, 0, 1)
+FIELD(FPSR, ENA_U, 1, 1)
+FIELD(FPSR, ENA_O, 2, 1)
+FIELD(FPSR, ENA_Z, 3, 1)
+FIELD(FPSR, ENA_V, 4, 1)
+FIELD(FPSR, ENABLES, 0, 5)
+FIELD(FPSR, D, 5, 1)
+FIELD(FPSR, T, 6, 1)
+FIELD(FPSR, RM, 9, 2)
+FIELD(FPSR, CQ, 11, 11)
+FIELD(FPSR, CQ0_6, 15, 7)
+FIELD(FPSR, CQ0_4, 17, 5)
+FIELD(FPSR, CQ0_2, 19, 3)
+FIELD(FPSR, CQ0, 21, 1)
+FIELD(FPSR, CA, 15, 7)
+FIELD(FPSR, CA0, 21, 1)
+FIELD(FPSR, C, 26, 1)
+FIELD(FPSR, FLG_I, 27, 1)
+FIELD(FPSR, FLG_U, 28, 1)
+FIELD(FPSR, FLG_O, 29, 1)
+FIELD(FPSR, FLG_Z, 30, 1)
+FIELD(FPSR, FLG_V, 31, 1)
+FIELD(FPSR, FLAGS, 27, 5)
+
typedef struct HPPATLBEntry {
union {
IntervalTreeNode itree;
@@ -180,7 +208,8 @@ typedef struct CPUArchState {
uint64_t fr[32];
uint64_t sr[8]; /* stored shifted into place for gva */
- target_ulong psw; /* All psw bits except the following: */
+ uint32_t psw; /* All psw bits except the following: */
+ uint32_t psw_xb; /* X and B, in their normal positions */
target_ulong psw_n; /* boolean */
target_long psw_v; /* in most significant bit */
@@ -313,48 +342,11 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr);
#define TB_FLAG_SR_SAME PSW_I
#define TB_FLAG_PRIV_SHIFT 8
#define TB_FLAG_UNALIGN 0x400
+#define CS_BASE_DIFFPAGE (1 << 12)
+#define CS_BASE_DIFFSPACE (1 << 13)
-static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
- uint64_t *cs_base, uint32_t *pflags)
-{
- uint32_t flags = env->psw_n * PSW_N;
-
- /* TB lookup assumes that PC contains the complete virtual address.
- If we leave space+offset separate, we'll get ITLB misses to an
- incomplete virtual address. This also means that we must separate
- out current cpu privilege from the low bits of IAOQ_F. */
-#ifdef CONFIG_USER_ONLY
- *pc = env->iaoq_f & -4;
- *cs_base = env->iaoq_b & -4;
- flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus;
-#else
- /* ??? E, T, H, L, B bits need to be here, when implemented. */
- flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P);
- flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT;
-
- *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0),
- env->iaoq_f & -4);
- *cs_base = env->iasq_f;
-
- /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero
- low 32-bits of CS_BASE. This will succeed for all direct branches,
- which is the primary case we care about -- using goto_tb within a page.
- Failure is indicated by a zero difference. */
- if (env->iasq_f == env->iasq_b) {
- target_long diff = env->iaoq_b - env->iaoq_f;
- if (diff == (int32_t)diff) {
- *cs_base |= (uint32_t)diff;
- }
- }
- if ((env->sr[4] == env->sr[5])
- & (env->sr[4] == env->sr[6])
- & (env->sr[4] == env->sr[7])) {
- flags |= TB_FLAG_SR_SAME;
- }
-#endif
-
- *pflags = flags;
-}
+void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
+ uint64_t *cs_base, uint32_t *pflags);
target_ulong cpu_hppa_get_psw(CPUHPPAState *env);
void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong);
@@ -379,8 +371,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
void hppa_cpu_do_interrupt(CPUState *cpu);
bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
- int type, hwaddr *pphys, int *pprot,
- HPPATLBEntry **tlb_entry);
+ int type, hwaddr *pphys, int *pprot);
void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
vaddr addr, unsigned size,
MMUAccessType access_type,
@@ -389,7 +380,6 @@ void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
extern const MemoryRegionOps hppa_io_eir_ops;
extern const VMStateDescription vmstate_hppa_cpu;
void hppa_cpu_alarm_timer(void *);
-int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr);
#endif
G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra);
diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c
index 576f283b04..deaed2b65d 100644
--- a/target/hppa/fpu_helper.c
+++ b/target/hppa/fpu_helper.c
@@ -30,7 +30,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env)
env->fr0_shadow = shadow;
- switch (extract32(shadow, 9, 2)) {
+ switch (FIELD_EX32(shadow, FPSR, RM)) {
default:
rm = float_round_nearest_even;
break;
@@ -46,7 +46,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env)
}
set_float_rounding_mode(rm, &env->fp_status);
- d = extract32(shadow, 5, 1);
+ d = FIELD_EX32(shadow, FPSR, D);
set_flush_to_zero(d, &env->fp_status);
set_flush_inputs_to_zero(d, &env->fp_status);
}
@@ -57,7 +57,7 @@ void cpu_hppa_loaded_fr0(CPUHPPAState *env)
}
#define CONVERT_BIT(X, SRC, DST) \
- ((SRC) > (DST) \
+ ((unsigned)(SRC) > (unsigned)(DST) \
? (X) / ((SRC) / (DST)) & (DST) \
: ((X) & (SRC)) * ((DST) / (SRC)))
@@ -73,12 +73,12 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
}
set_float_exception_flags(0, &env->fp_status);
- hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0);
- hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1);
- hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2);
- hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3);
- hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4);
- shadow |= hard_exp << (32 - 5);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK);
+ shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
env->fr0_shadow = shadow;
env->fr[0] = (uint64_t)shadow << 32;
@@ -378,15 +378,15 @@ static void update_fr0_cmp(CPUHPPAState *env, uint32_t y,
if (y) {
/* targeted comparison */
/* set fpsr[ca[y - 1]] to current compare */
- shadow = deposit32(shadow, 21 - (y - 1), 1, c);
+ shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c);
} else {
/* queued comparison */
/* shift cq right by one place */
- shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10));
+ shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK);
/* move fpsr[c] to fpsr[cq[0]] */
- shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1));
+ shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C));
/* set fpsr[c] to current compare */
- shadow = deposit32(shadow, 26, 1, c);
+ shadow = FIELD_DP32(shadow, FPSR, C, c);
}
env->fr0_shadow = shadow;
diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c
index 4a965b38d7..0daa52f7af 100644
--- a/target/hppa/gdbstub.c
+++ b/target/hppa/gdbstub.c
@@ -163,12 +163,18 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
env->cr[CR_SAR] = val & (hppa_is_pa20(env) ? 63 : 31);
break;
case 33:
+#ifdef CONFIG_USER_ONLY
+ val |= PRIV_USER;
+#endif
env->iaoq_f = val;
break;
case 34:
env->iasq_f = (uint64_t)val << 32;
break;
case 35:
+#ifdef CONFIG_USER_ONLY
+ val |= PRIV_USER;
+#endif
env->iaoq_b = val;
break;
case 36:
diff --git a/target/hppa/helper.c b/target/hppa/helper.c
index 9d217d051c..b79ddd8184 100644
--- a/target/hppa/helper.c
+++ b/target/hppa/helper.c
@@ -54,7 +54,7 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env)
psw |= env->psw_n * PSW_N;
psw |= (env->psw_v < 0) * PSW_V;
- psw |= env->psw;
+ psw |= env->psw | env->psw_xb;
return psw;
}
@@ -76,8 +76,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
}
psw &= ~reserved;
- env->psw = psw & (uint32_t)~(PSW_N | PSW_V | PSW_CB);
-
+ env->psw = psw & (uint32_t)~(PSW_B | PSW_N | PSW_V | PSW_X | PSW_CB);
+ env->psw_xb = psw & (PSW_X | PSW_B);
env->psw_n = (psw / PSW_N) & 1;
env->psw_v = -((psw / PSW_V) & 1);
@@ -102,6 +102,19 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
+#ifndef CONFIG_USER_ONLY
+ static const char cr_name[32][5] = {
+ "RC", "CR1", "CR2", "CR3",
+ "CR4", "CR5", "CR6", "CR7",
+ "PID1", "PID2", "CCR", "SAR",
+ "PID3", "PID4", "IVA", "EIEM",
+ "ITMR", "ISQF", "IOQF", "IIR",
+ "ISR", "IOR", "IPSW", "EIRR",
+ "TR0", "TR1", "TR2", "TR3",
+ "TR4", "TR5", "TR6", "TR7",
+ };
+#endif
+
CPUHPPAState *env = cpu_env(cs);
target_ulong psw = cpu_hppa_get_psw(env);
target_ulong psw_cb;
@@ -117,11 +130,12 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
m = UINT32_MAX;
}
- qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx
- " IIR %0*" PRIx64 "\n",
+ qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n"
+ "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n",
+ env->iasq_f >> 32, w, m & env->iaoq_f,
hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f),
- hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b),
- w, m & env->cr[CR_IIR]);
+ env->iasq_b >> 32, w, m & env->iaoq_b,
+ hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b));
psw_c[0] = (psw & PSW_W ? 'W' : '-');
psw_c[1] = (psw & PSW_E ? 'E' : '-');
@@ -154,12 +168,46 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
(i & 3) == 3 ? '\n' : ' ');
}
#ifndef CONFIG_USER_ONLY
+ for (i = 0; i < 32; i++) {
+ qemu_fprintf(f, "%-4s %0*" PRIx64 "%c",
+ cr_name[i], w, m & env->cr[i],
+ (i & 3) == 3 ? '\n' : ' ');
+ }
+ qemu_fprintf(f, "ISQB %0*" PRIx64 " IOQB %0*" PRIx64 "\n",
+ w, m & env->cr_back[0], w, m & env->cr_back[1]);
for (i = 0; i < 8; i++) {
qemu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32),
(i & 3) == 3 ? '\n' : ' ');
}
#endif
- qemu_fprintf(f, "\n");
- /* ??? FR */
+ if (flags & CPU_DUMP_FPU) {
+ static const char rm[4][4] = { "RN", "RZ", "R+", "R-" };
+ char flg[6], ena[6];
+ uint32_t fpsr = env->fr0_shadow;
+
+ flg[0] = (fpsr & R_FPSR_FLG_V_MASK ? 'V' : '-');
+ flg[1] = (fpsr & R_FPSR_FLG_Z_MASK ? 'Z' : '-');
+ flg[2] = (fpsr & R_FPSR_FLG_O_MASK ? 'O' : '-');
+ flg[3] = (fpsr & R_FPSR_FLG_U_MASK ? 'U' : '-');
+ flg[4] = (fpsr & R_FPSR_FLG_I_MASK ? 'I' : '-');
+ flg[5] = '\0';
+
+ ena[0] = (fpsr & R_FPSR_ENA_V_MASK ? 'V' : '-');
+ ena[1] = (fpsr & R_FPSR_ENA_Z_MASK ? 'Z' : '-');
+ ena[2] = (fpsr & R_FPSR_ENA_O_MASK ? 'O' : '-');
+ ena[3] = (fpsr & R_FPSR_ENA_U_MASK ? 'U' : '-');
+ ena[4] = (fpsr & R_FPSR_ENA_I_MASK ? 'I' : '-');
+ ena[5] = '\0';
+
+ qemu_fprintf(f, "FPSR %08x flag %s enable %s %s\n",
+ fpsr, flg, ena, rm[FIELD_EX32(fpsr, FPSR, RM)]);
+
+ for (i = 0; i < 32; i++) {
+ qemu_fprintf(f, "FR%02d %016" PRIx64 "%c",
+ i, env->fr[i], (i & 3) == 3 ? '\n' : ' ');
+ }
+ }
+
+ qemu_fprintf(f, "\n");
}
diff --git a/target/hppa/helper.h b/target/hppa/helper.h
index 5900fd70bc..de411923d9 100644
--- a/target/hppa/helper.h
+++ b/target/hppa/helper.h
@@ -1,6 +1,4 @@
DEF_HELPER_2(excp, noreturn, env, int)
-DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl)
-DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl)
DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl)
DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl)
@@ -88,6 +86,7 @@ DEF_HELPER_1(halt, noreturn, env)
DEF_HELPER_1(reset, noreturn, env)
DEF_HELPER_1(rfi, void, env)
DEF_HELPER_1(rfi_r, void, env)
+DEF_HELPER_FLAGS_2(b_gate_priv, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl)
diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c
index a667ee380d..391f32f27d 100644
--- a/target/hppa/int_helper.c
+++ b/target/hppa/int_helper.c
@@ -134,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs)
switch (i) {
case EXCP_ILL:
case EXCP_BREAK:
+ case EXCP_OVERFLOW:
+ case EXCP_COND:
case EXCP_PRIV_REG:
case EXCP_PRIV_OPR:
/* IIR set via translate.c. */
break;
- case EXCP_OVERFLOW:
- case EXCP_COND:
case EXCP_ASSIST:
case EXCP_DTLB_MISS:
case EXCP_NA_ITLB_MISS:
@@ -167,7 +167,7 @@ void hppa_cpu_do_interrupt(CPUState *cs)
vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr);
t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX,
- 0, &paddr, &prot, NULL);
+ 0, &paddr, &prot);
if (t >= 0) {
/* We can't re-load the instruction. */
env->cr[CR_IIR] = 0;
@@ -241,21 +241,22 @@ void hppa_cpu_do_interrupt(CPUState *cs)
[EXCP_SYSCALL_LWS] = "syscall-lws",
[EXCP_TOC] = "TOC (transfer of control)",
};
- static int count;
- const char *name = NULL;
- char unknown[16];
- if (i >= 0 && i < ARRAY_SIZE(names)) {
- name = names[i];
- }
- if (!name) {
- snprintf(unknown, sizeof(unknown), "unknown %d", i);
- name = unknown;
+ FILE *logfile = qemu_log_trylock();
+ if (logfile) {
+ const char *name = NULL;
+
+ if (i >= 0 && i < ARRAY_SIZE(names)) {
+ name = names[i];
+ }
+ if (name) {
+ fprintf(logfile, "INT: cpu %d %s\n", cs->cpu_index, name);
+ } else {
+ fprintf(logfile, "INT: cpu %d unknown %d\n", cs->cpu_index, i);
+ }
+ hppa_cpu_dump_state(cs, logfile, 0);
+ qemu_log_unlock(logfile);
}
- qemu_log("INT %6d: %s @ " TARGET_FMT_lx ":" TARGET_FMT_lx
- " for " TARGET_FMT_lx ":" TARGET_FMT_lx "\n",
- ++count, name, env->cr[CR_IIASQ], env->cr[CR_IIAOQ],
- env->cr[CR_ISR], env->cr[CR_IOR]);
}
cs->exception_index = -1;
}
diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c
index 84785b5a5c..b984f730aa 100644
--- a/target/hppa/mem_helper.c
+++ b/target/hppa/mem_helper.c
@@ -21,6 +21,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/helper-proto.h"
#include "hw/core/cpu.h"
#include "trace.h"
@@ -196,18 +197,13 @@ static int match_prot_id64(CPUHPPAState *env, uint32_t access_id)
}
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
- int type, hwaddr *pphys, int *pprot,
- HPPATLBEntry **tlb_entry)
+ int type, hwaddr *pphys, int *pprot)
{
hwaddr phys;
int prot, r_prot, w_prot, x_prot, priv;
HPPATLBEntry *ent;
int ret = -1;
- if (tlb_entry) {
- *tlb_entry = NULL;
- }
-
/* Virtual translation disabled. Map absolute to physical. */
if (MMU_IDX_MMU_DISABLED(mmu_idx)) {
switch (mmu_idx) {
@@ -237,10 +233,6 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
goto egress;
}
- if (tlb_entry) {
- *tlb_entry = ent;
- }
-
/* We now know the physical address. */
phys = ent->pa + (addr - ent->itree.start);
@@ -295,30 +287,38 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
goto egress;
}
- /* In reverse priority order, check for conditions which raise faults.
- As we go, remove PROT bits that cover the condition we want to check.
- In this way, the resulting PROT will force a re-check of the
- architectural TLB entry for the next access. */
- if (unlikely(!ent->d)) {
+ /*
+ * In priority order, check for conditions which raise faults.
+ * Remove PROT bits that cover the condition we want to check,
+ * so that the resulting PROT will force a re-check of the
+ * architectural TLB entry for the next access.
+ */
+ if (unlikely(ent->t)) {
+ prot &= PAGE_EXEC;
+ if (!(type & PAGE_EXEC)) {
+ /* The T bit is set -- Page Reference Fault. */
+ ret = EXCP_PAGE_REF;
+ }
+ } else if (!ent->d) {
+ prot &= PAGE_READ | PAGE_EXEC;
if (type & PAGE_WRITE) {
/* The D bit is not set -- TLB Dirty Bit Fault. */
ret = EXCP_TLB_DIRTY;
}
+ } else if (unlikely(ent->b)) {
prot &= PAGE_READ | PAGE_EXEC;
- }
- if (unlikely(ent->b)) {
if (type & PAGE_WRITE) {
- /* The B bit is set -- Data Memory Break Fault. */
- ret = EXCP_DMB;
- }
- prot &= PAGE_READ | PAGE_EXEC;
- }
- if (unlikely(ent->t)) {
- if (!(type & PAGE_EXEC)) {
- /* The T bit is set -- Page Reference Fault. */
- ret = EXCP_PAGE_REF;
+ /*
+ * The B bit is set -- Data Memory Break Fault.
+ * Except when PSW_X is set, allow this single access to succeed.
+ * The write bit will be invalidated for subsequent accesses.
+ */
+ if (env->psw_xb & PSW_X) {
+ prot |= PAGE_WRITE_INV;
+ } else {
+ ret = EXCP_DMB;
+ }
}
- prot &= PAGE_EXEC;
}
egress:
@@ -341,7 +341,7 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX);
excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0,
- &phys, &prot, NULL);
+ &phys, &prot);
/* Since we're translating for debugging, the only error that is a
hard error is no translation at all. Otherwise, while a real cpu
@@ -423,7 +423,6 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
{
HPPACPU *cpu = HPPA_CPU(cs);
CPUHPPAState *env = &cpu->env;
- HPPATLBEntry *ent;
int prot, excp, a_prot;
hwaddr phys;
@@ -439,8 +438,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
break;
}
- excp = hppa_get_physical_address(env, addr, mmu_idx,
- a_prot, &phys, &prot, &ent);
+ excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, &phys, &prot);
if (unlikely(excp >= 0)) {
if (probe) {
return false;
@@ -681,7 +679,7 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
int prot, excp;
excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0,
- &phys, &prot, NULL);
+ &phys, &prot);
if (excp >= 0) {
if (excp == EXCP_DTLB_MISS) {
excp = EXCP_NA_DTLB_MISS;
@@ -693,13 +691,6 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
return phys;
}
-/* Return the ar_type of the TLB at VADDR, or -1. */
-int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr)
-{
- HPPATLBEntry *ent = hppa_find_tlb(env, vaddr);
- return ent ? ent->ar_type : -1;
-}
-
/*
* diag_btlb() emulates the PDC PDC_BLOCK_TLB firmware call to
* allow operating systems to modify the Block TLB (BTLB) entries.
@@ -795,3 +786,30 @@ void HELPER(diag_btlb)(CPUHPPAState *env)
break;
}
}
+
+uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f)
+{
+ uint64_t gva = hppa_form_gva(env, env->iasq_f, iaoq_f);
+ HPPATLBEntry *ent = hppa_find_tlb(env, gva);
+
+ if (ent == NULL) {
+ raise_exception_with_ior(env, EXCP_ITLB_MISS, GETPC(), gva, false);
+ }
+
+ /*
+ * There should be no need to check page permissions, as that will
+ * already have been done by tb_lookup via get_page_addr_code.
+ * All we need at this point is to check the ar_type.
+ *
+ * No change for non-gateway pages or for priv decrease.
+ */
+ if (ent->ar_type & 4) {
+ int old_priv = iaoq_f & 3;
+ int new_priv = ent->ar_type & 3;
+
+ if (new_priv < old_priv) {
+ iaoq_f = (iaoq_f & -4) | new_priv;
+ }
+ }
+ return iaoq_f;
+}
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 6cf49f33b7..7f79196fff 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -42,20 +42,6 @@ G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra)
cpu_loop_exit_restore(cs, ra);
}
-void HELPER(tsv)(CPUHPPAState *env, target_ulong cond)
-{
- if (unlikely((target_long)cond < 0)) {
- hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC());
- }
-}
-
-void HELPER(tcond)(CPUHPPAState *env, target_ulong cond)
-{
- if (unlikely(cond)) {
- hppa_dynamic_excp(env, EXCP_COND, GETPC());
- }
-}
-
static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr,
uint32_t val, uint32_t mask, uintptr_t ra)
{
@@ -348,8 +334,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr,
}
mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P);
- excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys,
- &prot, NULL);
+ excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, &prot);
if (excp >= 0) {
cpu_restore_state(env_cpu(env), GETPC());
hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx));
diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c
index 22d6c89964..9b43b556fd 100644
--- a/target/hppa/sys_helper.c
+++ b/target/hppa/sys_helper.c
@@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
@@ -93,6 +94,17 @@ void HELPER(rfi)(CPUHPPAState *env)
env->iaoq_b = env->cr_back[1];
env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask);
env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask);
+
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ FILE *logfile = qemu_log_trylock();
+ if (logfile) {
+ CPUState *cs = env_cpu(env);
+
+ fprintf(logfile, "RFI: cpu %d\n", cs->cpu_index);
+ hppa_cpu_dump_state(cs, logfile, 0);
+ qemu_log_unlock(logfile);
+ }
+ }
}
static void getshadowregs(CPUHPPAState *env)
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index 42fa480950..51c1762435 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -19,9 +19,9 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "qemu/host-utils.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
#include "exec/helper-proto.h"
@@ -41,25 +41,51 @@ typedef struct DisasCond {
TCGv_i64 a0, a1;
} DisasCond;
+typedef struct DisasIAQE {
+ /* IASQ; may be null for no change from TB. */
+ TCGv_i64 space;
+ /* IAOQ base; may be null for relative address. */
+ TCGv_i64 base;
+ /* IAOQ addend; if base is null, relative to cpu_iaoq_f. */
+ int64_t disp;
+} DisasIAQE;
+
+typedef struct DisasDelayException {
+ struct DisasDelayException *next;
+ TCGLabel *lab;
+ uint32_t insn;
+ bool set_iir;
+ int8_t set_n;
+ uint8_t excp;
+ /* Saved state at parent insn. */
+ DisasIAQE iaq_f, iaq_b;
+} DisasDelayException;
+
typedef struct DisasContext {
DisasContextBase base;
CPUState *cs;
- uint64_t iaoq_f;
- uint64_t iaoq_b;
- uint64_t iaoq_n;
- TCGv_i64 iaoq_n_var;
+ /* IAQ_Front, IAQ_Back. */
+ DisasIAQE iaq_f, iaq_b;
+ /* IAQ_Next, for jumps, otherwise null for simple advance. */
+ DisasIAQE iaq_j, *iaq_n;
+
+ /* IAOQ_Front at entry to TB. */
+ uint64_t iaoq_first;
DisasCond null_cond;
TCGLabel *null_lab;
+ DisasDelayException *delay_excp_list;
TCGv_i64 zero;
uint32_t insn;
uint32_t tb_flags;
int mmu_idx;
int privilege;
+ uint32_t psw_xb;
bool psw_n_nonzero;
+ bool psw_b_next;
bool is_pa20;
bool insn_start_updated;
@@ -238,6 +264,7 @@ static TCGv_i64 cpu_psw_n;
static TCGv_i64 cpu_psw_v;
static TCGv_i64 cpu_psw_cb;
static TCGv_i64 cpu_psw_cb_msb;
+static TCGv_i32 cpu_psw_xb;
void hppa_translate_init(void)
{
@@ -290,6 +317,9 @@ void hppa_translate_init(void)
*v->var = tcg_global_mem_new(tcg_env, v->ofs, v->name);
}
+ cpu_psw_xb = tcg_global_mem_new_i32(tcg_env,
+ offsetof(CPUHPPAState, psw_xb),
+ "psw_xb");
cpu_iasq_f = tcg_global_mem_new_i64(tcg_env,
offsetof(CPUHPPAState, iasq_f),
"iasq_f");
@@ -332,47 +362,32 @@ static DisasCond cond_make_n(void)
};
}
-static DisasCond cond_make_tmp(TCGCond c, TCGv_i64 a0, TCGv_i64 a1)
+static DisasCond cond_make_tt(TCGCond c, TCGv_i64 a0, TCGv_i64 a1)
{
assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS);
return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 };
}
-static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0)
+static DisasCond cond_make_ti(TCGCond c, TCGv_i64 a0, uint64_t imm)
{
- return cond_make_tmp(c, a0, tcg_constant_i64(0));
+ return cond_make_tt(c, a0, tcg_constant_i64(imm));
}
-static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0)
+static DisasCond cond_make_vi(TCGCond c, TCGv_i64 a0, uint64_t imm)
{
TCGv_i64 tmp = tcg_temp_new_i64();
tcg_gen_mov_i64(tmp, a0);
- return cond_make_0_tmp(c, tmp);
+ return cond_make_ti(c, tmp, imm);
}
-static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1)
+static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1)
{
TCGv_i64 t0 = tcg_temp_new_i64();
TCGv_i64 t1 = tcg_temp_new_i64();
tcg_gen_mov_i64(t0, a0);
tcg_gen_mov_i64(t1, a1);
- return cond_make_tmp(c, t0, t1);
-}
-
-static void cond_free(DisasCond *cond)
-{
- switch (cond->c) {
- default:
- cond->a0 = NULL;
- cond->a1 = NULL;
- /* fallthru */
- case TCG_COND_ALWAYS:
- cond->c = TCG_COND_NEVER;
- break;
- case TCG_COND_NEVER:
- break;
- }
+ return cond_make_tt(c, t0, t1);
}
static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg)
@@ -499,6 +514,25 @@ static void load_spr(DisasContext *ctx, TCGv_i64 dest, unsigned reg)
#endif
}
+/*
+ * Write a value to psw_xb, bearing in mind the known value.
+ * To be used just before exiting the TB, so do not update the known value.
+ */
+static void store_psw_xb(DisasContext *ctx, uint32_t xb)
+{
+ tcg_debug_assert(xb == 0 || xb == PSW_B);
+ if (ctx->psw_xb != xb) {
+ tcg_gen_movi_i32(cpu_psw_xb, xb);
+ }
+}
+
+/* Write a value to psw_xb, and update the known value. */
+static void set_psw_xb(DisasContext *ctx, uint32_t xb)
+{
+ store_psw_xb(ctx, xb);
+ ctx->psw_xb = xb;
+}
+
/* Skip over the implementation of an insn that has been nullified.
Use this when the insn is too complex for a conditional move. */
static void nullify_over(DisasContext *ctx)
@@ -524,7 +558,7 @@ static void nullify_over(DisasContext *ctx)
tcg_gen_brcond_i64(ctx->null_cond.c, ctx->null_cond.a0,
ctx->null_cond.a1, ctx->null_lab);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
}
}
@@ -542,7 +576,7 @@ static void nullify_save(DisasContext *ctx)
ctx->null_cond.a0, ctx->null_cond.a1);
ctx->psw_n_nonzero = true;
}
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
}
/* Set a PSW[N] to X. The intention is that this is used immediately
@@ -566,6 +600,8 @@ static bool nullify_end(DisasContext *ctx)
/* For NEXT, NORETURN, STALE, we can easily continue (or exit).
For UPDATED, we cannot update on the nullified path. */
assert(status != DISAS_IAQ_N_UPDATED);
+ /* Taken branches are handled manually. */
+ assert(!ctx->psw_b_next);
if (likely(null_lab == NULL)) {
/* The current insn wasn't conditional or handled the condition
@@ -594,31 +630,94 @@ static bool nullify_end(DisasContext *ctx)
return true;
}
+static bool iaqe_variable(const DisasIAQE *e)
+{
+ return e->base || e->space;
+}
+
+static DisasIAQE iaqe_incr(const DisasIAQE *e, int64_t disp)
+{
+ return (DisasIAQE){
+ .space = e->space,
+ .base = e->base,
+ .disp = e->disp + disp,
+ };
+}
+
+static DisasIAQE iaqe_branchi(DisasContext *ctx, int64_t disp)
+{
+ return (DisasIAQE){
+ .space = ctx->iaq_b.space,
+ .disp = ctx->iaq_f.disp + 8 + disp,
+ };
+}
+
+static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var)
+{
+ return (DisasIAQE){
+ .space = ctx->iaq_b.space,
+ .base = var,
+ };
+}
+
static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest,
- uint64_t ival, TCGv_i64 vval)
+ const DisasIAQE *src)
{
- uint64_t mask = gva_offset_mask(ctx->tb_flags);
+ tcg_gen_addi_i64(dest, src->base ? : cpu_iaoq_f, src->disp);
+}
- if (ival != -1) {
- tcg_gen_movi_i64(dest, ival & mask);
- return;
+static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f,
+ const DisasIAQE *b)
+{
+ DisasIAQE b_next;
+
+ if (b == NULL) {
+ b_next = iaqe_incr(f, 4);
+ b = &b_next;
}
- tcg_debug_assert(vval != NULL);
/*
- * We know that the IAOQ is already properly masked.
- * This optimization is primarily for "iaoq_f = iaoq_b".
+ * There is an edge case
+ * bv r0(rN)
+ * b,l disp,r0
+ * for which F will use cpu_iaoq_b (from the indirect branch),
+ * and B will use cpu_iaoq_f (from the direct branch).
+ * In this case we need an extra temporary.
*/
- if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) {
- tcg_gen_mov_i64(dest, vval);
+ if (f->base != cpu_iaoq_b) {
+ copy_iaoq_entry(ctx, cpu_iaoq_b, b);
+ copy_iaoq_entry(ctx, cpu_iaoq_f, f);
+ } else if (f->base == b->base) {
+ copy_iaoq_entry(ctx, cpu_iaoq_f, f);
+ tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, b->disp - f->disp);
} else {
- tcg_gen_andi_i64(dest, vval, mask);
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ copy_iaoq_entry(ctx, tmp, b);
+ copy_iaoq_entry(ctx, cpu_iaoq_f, f);
+ tcg_gen_mov_i64(cpu_iaoq_b, tmp);
+ }
+
+ if (f->space) {
+ tcg_gen_mov_i64(cpu_iasq_f, f->space);
+ }
+ if (b->space || f->space) {
+ tcg_gen_mov_i64(cpu_iasq_b, b->space ? : f->space);
}
}
-static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp)
+static void install_link(DisasContext *ctx, unsigned link, bool with_sr0)
{
- return ctx->iaoq_f + disp + 8;
+ tcg_debug_assert(ctx->null_cond.c == TCG_COND_NEVER);
+ if (!link) {
+ return;
+ }
+ DisasIAQE next = iaqe_incr(&ctx->iaq_b, 4);
+ copy_iaoq_entry(ctx, cpu_gr[link], &next);
+#ifndef CONFIG_USER_ONLY
+ if (with_sr0) {
+ tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b);
+ }
+#endif
}
static void gen_excp_1(int exception)
@@ -628,20 +727,44 @@ static void gen_excp_1(int exception)
static void gen_excp(DisasContext *ctx, int exception)
{
- copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f);
- copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b);
+ install_iaq_entries(ctx, &ctx->iaq_f, &ctx->iaq_b);
nullify_save(ctx);
gen_excp_1(exception);
ctx->base.is_jmp = DISAS_NORETURN;
}
+static DisasDelayException *delay_excp(DisasContext *ctx, uint8_t excp)
+{
+ DisasDelayException *e = tcg_malloc(sizeof(DisasDelayException));
+
+ memset(e, 0, sizeof(*e));
+ e->next = ctx->delay_excp_list;
+ ctx->delay_excp_list = e;
+
+ e->lab = gen_new_label();
+ e->insn = ctx->insn;
+ e->set_iir = true;
+ e->set_n = ctx->psw_n_nonzero ? 0 : -1;
+ e->excp = excp;
+ e->iaq_f = ctx->iaq_f;
+ e->iaq_b = ctx->iaq_b;
+
+ return e;
+}
+
static bool gen_excp_iir(DisasContext *ctx, int exc)
{
- nullify_over(ctx);
- tcg_gen_st_i64(tcg_constant_i64(ctx->insn),
- tcg_env, offsetof(CPUHPPAState, cr[CR_IIR]));
- gen_excp(ctx, exc);
- return nullify_end(ctx);
+ if (ctx->null_cond.c == TCG_COND_NEVER) {
+ tcg_gen_st_i64(tcg_constant_i64(ctx->insn),
+ tcg_env, offsetof(CPUHPPAState, cr[CR_IIR]));
+ gen_excp(ctx, exc);
+ } else {
+ DisasDelayException *e = delay_excp(ctx, exc);
+ tcg_gen_brcond_i64(tcg_invert_cond(ctx->null_cond.c),
+ ctx->null_cond.a0, ctx->null_cond.a1, e->lab);
+ ctx->null_cond = cond_make_f();
+ }
+ return true;
}
static bool gen_illegal(DisasContext *ctx)
@@ -661,9 +784,12 @@ static bool gen_illegal(DisasContext *ctx)
} while (0)
#endif
-static bool use_goto_tb(DisasContext *ctx, uint64_t dest)
+static bool use_goto_tb(DisasContext *ctx, const DisasIAQE *f,
+ const DisasIAQE *b)
{
- return translator_use_goto_tb(&ctx->base, dest);
+ return (!iaqe_variable(f) &&
+ (b == NULL || !iaqe_variable(b)) &&
+ translator_use_goto_tb(&ctx->base, ctx->iaoq_first + f->disp));
}
/* If the next insn is to be nullified, and it's on the same page,
@@ -672,21 +798,20 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t dest)
executing a TB that merely branches to the next TB. */
static bool use_nullify_skip(DisasContext *ctx)
{
- return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0
- && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY));
+ return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE)
+ && !iaqe_variable(&ctx->iaq_b)
+ && (((ctx->iaoq_first + ctx->iaq_b.disp) ^ ctx->iaoq_first)
+ & TARGET_PAGE_MASK) == 0);
}
static void gen_goto_tb(DisasContext *ctx, int which,
- uint64_t f, uint64_t b)
+ const DisasIAQE *f, const DisasIAQE *b)
{
- if (f != -1 && b != -1 && use_goto_tb(ctx, f)) {
+ install_iaq_entries(ctx, f, b);
+ if (use_goto_tb(ctx, f, b)) {
tcg_gen_goto_tb(which);
- copy_iaoq_entry(ctx, cpu_iaoq_f, f, NULL);
- copy_iaoq_entry(ctx, cpu_iaoq_b, b, NULL);
tcg_gen_exit_tb(ctx->base.tb, which);
} else {
- copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b);
- copy_iaoq_entry(ctx, cpu_iaoq_b, b, ctx->iaoq_n_var);
tcg_gen_lookup_and_goto_ptr();
}
}
@@ -709,28 +834,36 @@ static bool cond_need_cb(int c)
static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d,
TCGv_i64 res, TCGv_i64 uv, TCGv_i64 sv)
{
+ TCGCond sign_cond, zero_cond;
+ uint64_t sign_imm, zero_imm;
DisasCond cond;
TCGv_i64 tmp;
+ if (d) {
+ /* 64-bit condition. */
+ sign_imm = 0;
+ sign_cond = TCG_COND_LT;
+ zero_imm = 0;
+ zero_cond = TCG_COND_EQ;
+ } else {
+ /* 32-bit condition. */
+ sign_imm = 1ull << 31;
+ sign_cond = TCG_COND_TSTNE;
+ zero_imm = UINT32_MAX;
+ zero_cond = TCG_COND_TSTEQ;
+ }
+
switch (cf >> 1) {
case 0: /* Never / TR (0 / 1) */
cond = cond_make_f();
break;
case 1: /* = / <> (Z / !Z) */
- if (!d) {
- tmp = tcg_temp_new_i64();
- tcg_gen_ext32u_i64(tmp, res);
- res = tmp;
- }
- cond = cond_make_0(TCG_COND_EQ, res);
+ cond = cond_make_vi(zero_cond, res, zero_imm);
break;
case 2: /* < / >= (N ^ V / !(N ^ V) */
tmp = tcg_temp_new_i64();
tcg_gen_xor_i64(tmp, res, sv);
- if (!d) {
- tcg_gen_ext32s_i64(tmp, tmp);
- }
- cond = cond_make_0_tmp(TCG_COND_LT, tmp);
+ cond = cond_make_ti(sign_cond, tmp, sign_imm);
break;
case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */
/*
@@ -738,45 +871,29 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d,
* (N ^ V) | Z
* ((res < 0) ^ (sv < 0)) | !res
* ((res ^ sv) < 0) | !res
- * (~(res ^ sv) >= 0) | !res
- * !(~(res ^ sv) >> 31) | !res
- * !(~(res ^ sv) >> 31 & res)
+ * ((res ^ sv) < 0 ? 1 : !res)
+ * !((res ^ sv) < 0 ? 0 : res)
*/
tmp = tcg_temp_new_i64();
- tcg_gen_eqv_i64(tmp, res, sv);
- if (!d) {
- tcg_gen_sextract_i64(tmp, tmp, 31, 1);
- tcg_gen_and_i64(tmp, tmp, res);
- tcg_gen_ext32u_i64(tmp, tmp);
- } else {
- tcg_gen_sari_i64(tmp, tmp, 63);
- tcg_gen_and_i64(tmp, tmp, res);
- }
- cond = cond_make_0_tmp(TCG_COND_EQ, tmp);
+ tcg_gen_xor_i64(tmp, res, sv);
+ tcg_gen_movcond_i64(sign_cond, tmp,
+ tmp, tcg_constant_i64(sign_imm),
+ ctx->zero, res);
+ cond = cond_make_ti(zero_cond, tmp, zero_imm);
break;
case 4: /* NUV / UV (!UV / UV) */
- cond = cond_make_0(TCG_COND_EQ, uv);
+ cond = cond_make_vi(TCG_COND_EQ, uv, 0);
break;
case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */
tmp = tcg_temp_new_i64();
tcg_gen_movcond_i64(TCG_COND_EQ, tmp, uv, ctx->zero, ctx->zero, res);
- if (!d) {
- tcg_gen_ext32u_i64(tmp, tmp);
- }
- cond = cond_make_0_tmp(TCG_COND_EQ, tmp);
+ cond = cond_make_ti(zero_cond, tmp, zero_imm);
break;
case 6: /* SV / NSV (V / !V) */
- if (!d) {
- tmp = tcg_temp_new_i64();
- tcg_gen_ext32s_i64(tmp, sv);
- sv = tmp;
- }
- cond = cond_make_0(TCG_COND_LT, sv);
+ cond = cond_make_vi(sign_cond, sv, sign_imm);
break;
case 7: /* OD / EV */
- tmp = tcg_temp_new_i64();
- tcg_gen_andi_i64(tmp, res, 1);
- cond = cond_make_0_tmp(TCG_COND_NE, tmp);
+ cond = cond_make_vi(TCG_COND_TSTNE, res, 1);
break;
default:
g_assert_not_reached();
@@ -838,9 +955,9 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d,
tcg_gen_ext32s_i64(t1, in1);
tcg_gen_ext32s_i64(t2, in2);
}
- return cond_make_tmp(tc, t1, t2);
+ return cond_make_tt(tc, t1, t2);
}
- return cond_make(tc, in1, in2);
+ return cond_make_vv(tc, in1, in2);
}
/*
@@ -856,65 +973,41 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d,
TCGv_i64 res)
{
TCGCond tc;
- bool ext_uns;
+ uint64_t imm;
- switch (cf) {
- case 0: /* never */
- case 9: /* undef, C */
- case 11: /* undef, C & !Z */
- case 12: /* undef, V */
- return cond_make_f();
-
- case 1: /* true */
- case 8: /* undef, !C */
- case 10: /* undef, !C | Z */
- case 13: /* undef, !V */
- return cond_make_t();
-
- case 2: /* == */
- tc = TCG_COND_EQ;
- ext_uns = true;
- break;
- case 3: /* <> */
- tc = TCG_COND_NE;
- ext_uns = true;
- break;
- case 4: /* < */
- tc = TCG_COND_LT;
- ext_uns = false;
- break;
- case 5: /* >= */
- tc = TCG_COND_GE;
- ext_uns = false;
+ switch (cf >> 1) {
+ case 0: /* never / always */
+ case 4: /* undef, C */
+ case 5: /* undef, C & !Z */
+ case 6: /* undef, V */
+ return cf & 1 ? cond_make_t() : cond_make_f();
+ case 1: /* == / <> */
+ tc = d ? TCG_COND_EQ : TCG_COND_TSTEQ;
+ imm = d ? 0 : UINT32_MAX;
break;
- case 6: /* <= */
- tc = TCG_COND_LE;
- ext_uns = false;
+ case 2: /* < / >= */
+ tc = d ? TCG_COND_LT : TCG_COND_TSTNE;
+ imm = d ? 0 : 1ull << 31;
break;
- case 7: /* > */
- tc = TCG_COND_GT;
- ext_uns = false;
+ case 3: /* <= / > */
+ tc = cf & 1 ? TCG_COND_GT : TCG_COND_LE;
+ if (!d) {
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ext32s_i64(tmp, res);
+ return cond_make_ti(tc, tmp, 0);
+ }
+ return cond_make_vi(tc, res, 0);
+ case 7: /* OD / EV */
+ tc = TCG_COND_TSTNE;
+ imm = 1;
break;
-
- case 14: /* OD */
- case 15: /* EV */
- return do_cond(ctx, cf, d, res, NULL, NULL);
-
default:
g_assert_not_reached();
}
-
- if (!d) {
- TCGv_i64 tmp = tcg_temp_new_i64();
-
- if (ext_uns) {
- tcg_gen_ext32u_i64(tmp, res);
- } else {
- tcg_gen_ext32s_i64(tmp, res);
- }
- return cond_make_0_tmp(tc, tmp);
+ if (cf & 1) {
+ tc = tcg_invert_cond(tc);
}
- return cond_make_0(tc, res);
+ return cond_make_vi(tc, res, imm);
}
/* Similar, but for shift/extract/deposit conditions. */
@@ -971,9 +1064,8 @@ static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res)
tmp = tcg_temp_new_i64();
tcg_gen_subi_i64(tmp, res, ones);
tcg_gen_andc_i64(tmp, tmp, res);
- tcg_gen_andi_i64(tmp, tmp, sgns);
- return cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp);
+ return cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, tmp, sgns);
}
static TCGv_i64 get_carry(DisasContext *ctx, bool d,
@@ -1061,6 +1153,36 @@ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res,
return sv;
}
+static void gen_tc(DisasContext *ctx, DisasCond *cond)
+{
+ DisasDelayException *e;
+
+ switch (cond->c) {
+ case TCG_COND_NEVER:
+ break;
+ case TCG_COND_ALWAYS:
+ gen_excp_iir(ctx, EXCP_COND);
+ break;
+ default:
+ e = delay_excp(ctx, EXCP_COND);
+ tcg_gen_brcond_i64(cond->c, cond->a0, cond->a1, e->lab);
+ /* In the non-trap path, the condition is known false. */
+ *cond = cond_make_f();
+ break;
+ }
+}
+
+static void gen_tsv(DisasContext *ctx, TCGv_i64 *sv, bool d)
+{
+ DisasCond cond = do_cond(ctx, /* SV */ 12, d, NULL, NULL, *sv);
+ DisasDelayException *e = delay_excp(ctx, EXCP_OVERFLOW);
+
+ tcg_gen_brcond_i64(cond.c, cond.a0, cond.a1, e->lab);
+
+ /* In the non-trap path, V is known zero. */
+ *sv = tcg_constant_i64(0);
+}
+
static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
TCGv_i64 in2, unsigned shift, bool is_l,
bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d)
@@ -1103,10 +1225,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
if (is_tsv || cond_need_sv(c)) {
sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d);
if (is_tsv) {
- if (!d) {
- tcg_gen_ext32s_i64(sv, sv);
- }
- gen_helper_tsv(tcg_env, sv);
+ gen_tsv(ctx, &sv, d);
}
}
@@ -1119,9 +1238,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
/* Emit any conditional trap before any writeback. */
cond = do_cond(ctx, cf, d, dest, uv, sv);
if (is_tc) {
- tmp = tcg_temp_new_i64();
- tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1);
- gen_helper_tcond(tcg_env, tmp);
+ gen_tc(ctx, &cond);
}
/* Write back the result. */
@@ -1132,7 +1249,6 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
ctx->null_cond = cond;
}
@@ -1141,6 +1257,10 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a,
{
TCGv_i64 tcg_r1, tcg_r2;
+ if (unlikely(is_tc && a->cf == 1)) {
+ /* Unconditional trap on condition. */
+ return gen_excp_iir(ctx, EXCP_COND);
+ }
if (a->cf) {
nullify_over(ctx);
}
@@ -1156,6 +1276,10 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a,
{
TCGv_i64 tcg_im, tcg_r2;
+ if (unlikely(is_tc && a->cf == 1)) {
+ /* Unconditional trap on condition. */
+ return gen_excp_iir(ctx, EXCP_COND);
+ }
if (a->cf) {
nullify_over(ctx);
}
@@ -1170,7 +1294,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
TCGv_i64 in2, bool is_tsv, bool is_b,
bool is_tc, unsigned cf, bool d)
{
- TCGv_i64 dest, sv, cb, cb_msb, tmp;
+ TCGv_i64 dest, sv, cb, cb_msb;
unsigned c = cf >> 1;
DisasCond cond;
@@ -1202,10 +1326,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
if (is_tsv || cond_need_sv(c)) {
sv = do_sub_sv(ctx, dest, in1, in2);
if (is_tsv) {
- if (!d) {
- tcg_gen_ext32s_i64(sv, sv);
- }
- gen_helper_tsv(tcg_env, sv);
+ gen_tsv(ctx, &sv, d);
}
}
@@ -1218,9 +1339,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
/* Emit any conditional trap before any writeback. */
if (is_tc) {
- tmp = tcg_temp_new_i64();
- tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1);
- gen_helper_tcond(tcg_env, tmp);
+ gen_tc(ctx, &cond);
}
/* Write back the result. */
@@ -1229,7 +1348,6 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
ctx->null_cond = cond;
}
@@ -1284,7 +1402,6 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
ctx->null_cond = cond;
}
@@ -1299,10 +1416,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (cf) {
- ctx->null_cond = do_log_cond(ctx, cf, d, dest);
- }
+ ctx->null_cond = do_log_cond(ctx, cf, d, dest);
}
static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a,
@@ -1386,18 +1500,15 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
tcg_gen_shri_i64(cb, cb, 1);
}
- tcg_gen_andi_i64(cb, cb, test_cb);
- cond = cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb);
+ cond = cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE,
+ cb, test_cb);
}
if (is_tc) {
- TCGv_i64 tmp = tcg_temp_new_i64();
- tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1);
- gen_helper_tcond(tcg_env, tmp);
+ gen_tc(ctx, &cond);
}
save_gpr(ctx, rt, dest);
- cond_free(&ctx->null_cond);
ctx->null_cond = cond;
}
@@ -1764,36 +1875,43 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt,
/* Emit an unconditional branch to a direct target, which may or may not
have already had nullification handled. */
-static bool do_dbranch(DisasContext *ctx, uint64_t dest,
+static bool do_dbranch(DisasContext *ctx, int64_t disp,
unsigned link, bool is_n)
{
+ ctx->iaq_j = iaqe_branchi(ctx, disp);
+
if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) {
- if (link != 0) {
- copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var);
- }
- ctx->iaoq_n = dest;
+ install_link(ctx, link, false);
if (is_n) {
+ if (use_nullify_skip(ctx)) {
+ nullify_set(ctx, 0);
+ store_psw_xb(ctx, 0);
+ gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return true;
+ }
ctx->null_cond.c = TCG_COND_ALWAYS;
}
+ ctx->iaq_n = &ctx->iaq_j;
+ ctx->psw_b_next = true;
} else {
nullify_over(ctx);
- if (link != 0) {
- copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var);
- }
-
+ install_link(ctx, link, false);
if (is_n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0);
- gen_goto_tb(ctx, 0, dest, dest + 4);
+ store_psw_xb(ctx, 0);
+ gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL);
} else {
nullify_set(ctx, is_n);
- gen_goto_tb(ctx, 0, ctx->iaoq_b, dest);
+ store_psw_xb(ctx, PSW_B);
+ gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j);
}
-
nullify_end(ctx);
nullify_set(ctx, 0);
- gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n);
+ store_psw_xb(ctx, 0);
+ gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL);
ctx->base.is_jmp = DISAS_NORETURN;
}
return true;
@@ -1804,7 +1922,7 @@ static bool do_dbranch(DisasContext *ctx, uint64_t dest,
static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
DisasCond *cond)
{
- uint64_t dest = iaoq_dest(ctx, disp);
+ DisasIAQE next;
TCGLabel *taken = NULL;
TCGCond c = cond->c;
bool n;
@@ -1813,45 +1931,43 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
/* Handle TRUE and NEVER as direct branches. */
if (c == TCG_COND_ALWAYS) {
- return do_dbranch(ctx, dest, 0, is_n && disp >= 0);
- }
- if (c == TCG_COND_NEVER) {
- return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0);
+ return do_dbranch(ctx, disp, 0, is_n && disp >= 0);
}
taken = gen_new_label();
tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken);
- cond_free(cond);
/* Not taken: Condition not satisfied; nullify on backward branches. */
n = is_n && disp < 0;
if (n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0);
- gen_goto_tb(ctx, 0, ctx->iaoq_n, ctx->iaoq_n + 4);
+ store_psw_xb(ctx, 0);
+ next = iaqe_incr(&ctx->iaq_b, 4);
+ gen_goto_tb(ctx, 0, &next, NULL);
} else {
if (!n && ctx->null_lab) {
gen_set_label(ctx->null_lab);
ctx->null_lab = NULL;
}
nullify_set(ctx, n);
- if (ctx->iaoq_n == -1) {
- /* The temporary iaoq_n_var died at the branch above.
- Regenerate it here instead of saving it. */
- tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4);
- }
- gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n);
+ store_psw_xb(ctx, 0);
+ gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL);
}
gen_set_label(taken);
/* Taken: Condition satisfied; nullify on forward branches. */
n = is_n && disp >= 0;
+
+ next = iaqe_branchi(ctx, disp);
if (n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0);
- gen_goto_tb(ctx, 1, dest, dest + 4);
+ store_psw_xb(ctx, 0);
+ gen_goto_tb(ctx, 1, &next, NULL);
} else {
nullify_set(ctx, n);
- gen_goto_tb(ctx, 1, ctx->iaoq_b, dest);
+ store_psw_xb(ctx, PSW_B);
+ gen_goto_tb(ctx, 1, &ctx->iaq_b, &next);
}
/* Not taken: the branch itself was nullified. */
@@ -1865,89 +1981,45 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
return true;
}
-/* Emit an unconditional branch to an indirect target. This handles
- nullification of the branch itself. */
-static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest,
- unsigned link, bool is_n)
+/*
+ * Emit an unconditional branch to an indirect target, in ctx->iaq_j.
+ * This handles nullification of the branch itself.
+ */
+static bool do_ibranch(DisasContext *ctx, unsigned link,
+ bool with_sr0, bool is_n)
{
- TCGv_i64 a0, a1, next, tmp;
- TCGCond c;
-
- assert(ctx->null_lab == NULL);
-
- if (ctx->null_cond.c == TCG_COND_NEVER) {
- if (link != 0) {
- copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var);
- }
- next = tcg_temp_new_i64();
- tcg_gen_mov_i64(next, dest);
+ if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) {
+ install_link(ctx, link, with_sr0);
if (is_n) {
if (use_nullify_skip(ctx)) {
- copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next);
- tcg_gen_addi_i64(next, next, 4);
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next);
+ install_iaq_entries(ctx, &ctx->iaq_j, NULL);
nullify_set(ctx, 0);
ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
return true;
}
ctx->null_cond.c = TCG_COND_ALWAYS;
}
- ctx->iaoq_n = -1;
- ctx->iaoq_n_var = next;
- } else if (is_n && use_nullify_skip(ctx)) {
- /* The (conditional) branch, B, nullifies the next insn, N,
- and we're allowed to skip execution N (no single-step or
- tracepoint in effect). Since the goto_ptr that we must use
- for the indirect branch consumes no special resources, we
- can (conditionally) skip B and continue execution. */
- /* The use_nullify_skip test implies we have a known control path. */
- tcg_debug_assert(ctx->iaoq_b != -1);
- tcg_debug_assert(ctx->iaoq_n != -1);
-
- /* We do have to handle the non-local temporary, DEST, before
- branching. Since IOAQ_F is not really live at this point, we
- can simply store DEST optimistically. Similarly with IAOQ_B. */
- copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest);
- next = tcg_temp_new_i64();
- tcg_gen_addi_i64(next, dest, 4);
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next);
-
- nullify_over(ctx);
- if (link != 0) {
- copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var);
- }
- tcg_gen_lookup_and_goto_ptr();
- return nullify_end(ctx);
- } else {
- c = ctx->null_cond.c;
- a0 = ctx->null_cond.a0;
- a1 = ctx->null_cond.a1;
-
- tmp = tcg_temp_new_i64();
- next = tcg_temp_new_i64();
-
- copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var);
- tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest);
- ctx->iaoq_n = -1;
- ctx->iaoq_n_var = next;
+ ctx->iaq_n = &ctx->iaq_j;
+ ctx->psw_b_next = true;
+ return true;
+ }
- if (link != 0) {
- tcg_gen_movcond_i64(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp);
- }
+ nullify_over(ctx);
- if (is_n) {
- /* The branch nullifies the next insn, which means the state of N
- after the branch is the inverse of the state of N that applied
- to the branch. */
- tcg_gen_setcond_i64(tcg_invert_cond(c), cpu_psw_n, a0, a1);
- cond_free(&ctx->null_cond);
- ctx->null_cond = cond_make_n();
- ctx->psw_n_nonzero = true;
- } else {
- cond_free(&ctx->null_cond);
- }
+ install_link(ctx, link, with_sr0);
+ if (is_n && use_nullify_skip(ctx)) {
+ install_iaq_entries(ctx, &ctx->iaq_j, NULL);
+ nullify_set(ctx, 0);
+ store_psw_xb(ctx, 0);
+ } else {
+ install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j);
+ nullify_set(ctx, is_n);
+ store_psw_xb(ctx, PSW_B);
}
- return true;
+
+ tcg_gen_lookup_and_goto_ptr();
+ ctx->base.is_jmp = DISAS_NORETURN;
+ return nullify_end(ctx);
}
/* Implement
@@ -1959,21 +2031,20 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest,
*/
static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset)
{
- TCGv_i64 dest;
+ TCGv_i64 dest = tcg_temp_new_i64();
switch (ctx->privilege) {
case 0:
/* Privilege 0 is maximum and is allowed to decrease. */
- return offset;
+ tcg_gen_mov_i64(dest, offset);
+ break;
case 3:
/* Privilege 3 is minimum and is never allowed to increase. */
- dest = tcg_temp_new_i64();
tcg_gen_ori_i64(dest, offset, 3);
break;
default:
- dest = tcg_temp_new_i64();
tcg_gen_andi_i64(dest, offset, -4);
tcg_gen_ori_i64(dest, dest, ctx->privilege);
- tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset);
+ tcg_gen_umax_i64(dest, dest, offset);
break;
}
return dest;
@@ -1989,7 +2060,7 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset)
aforementioned BE. */
static void do_page_zero(DisasContext *ctx)
{
- TCGv_i64 tmp;
+ assert(ctx->iaq_f.disp == 0);
/* If by some means we get here with PSW[N]=1, that implies that
the B,GATE instruction would be skipped, and we'd fault on the
@@ -2006,15 +2077,12 @@ static void do_page_zero(DisasContext *ctx)
g_assert_not_reached();
}
- /* Check that we didn't arrive here via some means that allowed
- non-sequential instruction execution. Normally the PSW[B] bit
- detects this by disallowing the B,GATE instruction to execute
- under such conditions. */
- if (ctx->iaoq_b != ctx->iaoq_f + 4) {
+ /* If PSW[B] is set, the B,GATE insn would trap. */
+ if (ctx->psw_xb & PSW_B) {
goto do_sigill;
}
- switch (ctx->iaoq_f & -4) {
+ switch (ctx->base.pc_first) {
case 0x00: /* Null pointer call */
gen_excp_1(EXCP_IMP);
ctx->base.is_jmp = DISAS_NORETURN;
@@ -2026,13 +2094,15 @@ static void do_page_zero(DisasContext *ctx)
break;
case 0xe0: /* SET_THREAD_POINTER */
- tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27]));
- tmp = tcg_temp_new_i64();
- tcg_gen_ori_i64(tmp, cpu_gr[31], 3);
- copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp);
- tcg_gen_addi_i64(tmp, tmp, 4);
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp);
- ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
+ {
+ DisasIAQE next = { .base = tcg_temp_new_i64() };
+
+ tcg_gen_st_i64(cpu_gr[26], tcg_env,
+ offsetof(CPUHPPAState, cr[27]));
+ tcg_gen_ori_i64(next.base, cpu_gr[31], PRIV_USER);
+ install_iaq_entries(ctx, &next, NULL);
+ ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
+ }
break;
case 0x100: /* SYSCALL */
@@ -2051,7 +2121,7 @@ static void do_page_zero(DisasContext *ctx)
static bool trans_nop(DisasContext *ctx, arg_nop *a)
{
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2065,18 +2135,19 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a)
/* No point in nullifying the memory barrier. */
tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
static bool trans_mfia(DisasContext *ctx, arg_mfia *a)
{
- unsigned rt = a->t;
- TCGv_i64 tmp = dest_gpr(ctx, rt);
- tcg_gen_movi_i64(tmp, ctx->iaoq_f & ~3ULL);
- save_gpr(ctx, rt, tmp);
+ TCGv_i64 dest = dest_gpr(ctx, a->t);
+
+ copy_iaoq_entry(ctx, dest, &ctx->iaq_f);
+ tcg_gen_andi_i64(dest, dest, -4);
- cond_free(&ctx->null_cond);
+ save_gpr(ctx, a->t, dest);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2091,7 +2162,7 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a)
save_gpr(ctx, rt, t0);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2136,7 +2207,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a)
save_gpr(ctx, rt, tmp);
done:
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2176,7 +2247,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31);
save_or_nullify(ctx, cpu_sar, tmp);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2250,7 +2321,7 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a)
tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31);
save_or_nullify(ctx, cpu_sar, tmp);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2267,7 +2338,7 @@ static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a)
#endif
save_gpr(ctx, a->t, dest);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2367,6 +2438,7 @@ static bool trans_halt(DisasContext *ctx, arg_halt *a)
{
CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
#ifndef CONFIG_USER_ONLY
+ set_psw_xb(ctx, 0);
nullify_over(ctx);
gen_helper_halt(tcg_env);
ctx->base.is_jmp = DISAS_NORETURN;
@@ -2378,6 +2450,7 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a)
{
CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
#ifndef CONFIG_USER_ONLY
+ set_psw_xb(ctx, 0);
nullify_over(ctx);
gen_helper_reset(tcg_env);
ctx->base.is_jmp = DISAS_NORETURN;
@@ -2429,7 +2502,7 @@ static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a)
tcg_gen_add_i64(dest, src1, src2);
save_gpr(ctx, a->b, dest);
}
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2671,7 +2744,7 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a)
since the entire address space is coherent. */
save_gpr(ctx, a->t, ctx->zero);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -2748,7 +2821,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a)
unsigned rt = a->t;
if (rt == 0) { /* NOP */
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
if (r2 == 0) { /* COPY */
@@ -2759,7 +2832,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a)
} else {
save_gpr(ctx, rt, cpu_gr[r1]);
}
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
#ifndef CONFIG_USER_ONLY
@@ -2772,11 +2845,13 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a)
if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */
/* No need to check for supervisor, as userland can only pause
until the next timer interrupt. */
+
+ set_psw_xb(ctx, 0);
+
nullify_over(ctx);
/* Advance the instruction queue. */
- copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
- copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+ install_iaq_entries(ctx, &ctx->iaq_b, NULL);
nullify_set(ctx, 0);
/* Tell the qemu main loop to halt until this cpu has work. */
@@ -2825,11 +2900,7 @@ static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a)
tcg_gen_xor_i64(dest, tcg_r1, tcg_r2);
save_gpr(ctx, a->t, dest);
- cond_free(&ctx->null_cond);
- if (a->cf) {
- ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest);
- }
-
+ ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest);
return nullify_end(ctx);
}
@@ -2855,7 +2926,7 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc)
tcg_gen_subi_i64(tmp, tmp, 1);
}
save_gpr(ctx, a->t, tmp);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -3381,7 +3452,7 @@ static bool trans_ldil(DisasContext *ctx, arg_ldil *a)
tcg_gen_movi_i64(tcg_rt, a->i);
save_gpr(ctx, a->t, tcg_rt);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -3392,7 +3463,7 @@ static bool trans_addil(DisasContext *ctx, arg_addil *a)
tcg_gen_addi_i64(tcg_r1, tcg_rt, a->i);
save_gpr(ctx, 1, tcg_r1);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -3408,7 +3479,7 @@ static bool trans_ldo(DisasContext *ctx, arg_ldo *a)
tcg_gen_addi_i64(tcg_rt, cpu_gr[a->b], a->i);
}
save_gpr(ctx, a->t, tcg_rt);
- cond_free(&ctx->null_cond);
+ ctx->null_cond = cond_make_f();
return true;
}
@@ -3525,24 +3596,18 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a)
tcg_gen_shl_i64(tmp, tcg_r, tmp);
}
- cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp);
+ cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0);
return do_cbranch(ctx, a->disp, a->n, &cond);
}
static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a)
{
- TCGv_i64 tmp, tcg_r;
DisasCond cond;
- int p;
+ int p = a->p | (a->d ? 0 : 32);
nullify_over(ctx);
-
- tmp = tcg_temp_new_i64();
- tcg_r = load_gpr(ctx, a->r);
- p = a->p | (a->d ? 0 : 32);
- tcg_gen_shli_i64(tmp, tcg_r, p);
-
- cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp);
+ cond = cond_make_vi(a->c ? TCG_COND_TSTEQ : TCG_COND_TSTNE,
+ load_gpr(ctx, a->r), 1ull << (63 - p));
return do_cbranch(ctx, a->disp, a->n, &cond);
}
@@ -3640,10 +3705,7 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3683,10 +3745,7 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3728,10 +3787,7 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3764,10 +3820,7 @@ static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3804,10 +3857,7 @@ static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3840,10 +3890,7 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a)
save_gpr(ctx, a->t, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (a->c) {
- ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest);
return nullify_end(ctx);
}
@@ -3877,10 +3924,7 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c,
save_gpr(ctx, rt, dest);
/* Install the new nullification. */
- cond_free(&ctx->null_cond);
- if (c) {
- ctx->null_cond = do_sed_cond(ctx, c, d, dest);
- }
+ ctx->null_cond = do_sed_cond(ctx, c, d, dest);
return nullify_end(ctx);
}
@@ -3910,104 +3954,53 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a)
static bool trans_be(DisasContext *ctx, arg_be *a)
{
- TCGv_i64 tmp;
-
-#ifdef CONFIG_USER_ONLY
- /* ??? It seems like there should be a good way of using
- "be disp(sr2, r0)", the canonical gateway entry mechanism
- to our advantage. But that appears to be inconvenient to
- manage along side branch delay slots. Therefore we handle
- entry into the gateway page via absolute address. */
- /* Since we don't implement spaces, just branch. Do notice the special
- case of "be disp(*,r0)" using a direct branch to disp, so that we can
- goto_tb to the TB containing the syscall. */
- if (a->b == 0) {
- return do_dbranch(ctx, a->disp, a->l, a->n);
- }
-#else
- nullify_over(ctx);
+#ifndef CONFIG_USER_ONLY
+ ctx->iaq_j.space = tcg_temp_new_i64();
+ load_spr(ctx, ctx->iaq_j.space, a->sp);
#endif
- tmp = tcg_temp_new_i64();
- tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp);
- tmp = do_ibranch_priv(ctx, tmp);
+ ctx->iaq_j.base = tcg_temp_new_i64();
+ ctx->iaq_j.disp = 0;
-#ifdef CONFIG_USER_ONLY
- return do_ibranch(ctx, tmp, a->l, a->n);
-#else
- TCGv_i64 new_spc = tcg_temp_new_i64();
+ tcg_gen_addi_i64(ctx->iaq_j.base, load_gpr(ctx, a->b), a->disp);
+ ctx->iaq_j.base = do_ibranch_priv(ctx, ctx->iaq_j.base);
- load_spr(ctx, new_spc, a->sp);
- if (a->l) {
- copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var);
- tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b);
- }
- if (a->n && use_nullify_skip(ctx)) {
- copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp);
- tcg_gen_addi_i64(tmp, tmp, 4);
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp);
- tcg_gen_mov_i64(cpu_iasq_f, new_spc);
- tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f);
- nullify_set(ctx, 0);
- } else {
- copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
- if (ctx->iaoq_b == -1) {
- tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
- }
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp);
- tcg_gen_mov_i64(cpu_iasq_b, new_spc);
- nullify_set(ctx, a->n);
- }
- tcg_gen_lookup_and_goto_ptr();
- ctx->base.is_jmp = DISAS_NORETURN;
- return nullify_end(ctx);
-#endif
+ return do_ibranch(ctx, a->l, true, a->n);
}
static bool trans_bl(DisasContext *ctx, arg_bl *a)
{
- return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n);
+ return do_dbranch(ctx, a->disp, a->l, a->n);
}
static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
{
- uint64_t dest = iaoq_dest(ctx, a->disp);
-
- nullify_over(ctx);
+ int64_t disp = a->disp;
+ bool indirect = false;
- /* Make sure the caller hasn't done something weird with the queue.
- * ??? This is not quite the same as the PSW[B] bit, which would be
- * expensive to track. Real hardware will trap for
- * b gateway
- * b gateway+4 (in delay slot of first branch)
- * However, checking for a non-sequential instruction queue *will*
- * diagnose the security hole
- * b gateway
- * b evil
- * in which instructions at evil would run with increased privs.
- */
- if (ctx->iaoq_b == -1 || ctx->iaoq_b != ctx->iaoq_f + 4) {
+ /* Trap if PSW[B] is set. */
+ if (ctx->psw_xb & PSW_B) {
return gen_illegal(ctx);
}
+ nullify_over(ctx);
+
#ifndef CONFIG_USER_ONLY
- if (ctx->tb_flags & PSW_C) {
- int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next);
- /* If we could not find a TLB entry, then we need to generate an
- ITLB miss exception so the kernel will provide it.
- The resulting TLB fill operation will invalidate this TB and
- we will re-translate, at which point we *will* be able to find
- the TLB entry and determine if this is in fact a gateway page. */
- if (type < 0) {
- gen_excp(ctx, EXCP_ITLB_MISS);
- return true;
- }
- /* No change for non-gateway pages or for priv decrease. */
- if (type >= 4 && type - 4 < ctx->privilege) {
- dest = deposit64(dest, 0, 2, type - 4);
- }
+ if (ctx->privilege == 0) {
+ /* Privilege cannot decrease. */
+ } else if (!(ctx->tb_flags & PSW_C)) {
+ /* With paging disabled, priv becomes 0. */
+ disp -= ctx->privilege;
} else {
- dest &= -4; /* priv = 0 */
+ /* Adjust the dest offset for the privilege change from the PTE. */
+ TCGv_i64 off = tcg_temp_new_i64();
+
+ copy_iaoq_entry(ctx, off, &ctx->iaq_f);
+ gen_helper_b_gate_priv(off, tcg_env, off);
+
+ ctx->iaq_j.base = off;
+ ctx->iaq_j.disp = disp + 8;
+ indirect = true;
}
#endif
@@ -4020,20 +4013,29 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
save_gpr(ctx, a->l, tmp);
}
- return do_dbranch(ctx, dest, 0, a->n);
+ if (indirect) {
+ return do_ibranch(ctx, 0, false, a->n);
+ }
+ return do_dbranch(ctx, disp, 0, a->n);
}
static bool trans_blr(DisasContext *ctx, arg_blr *a)
{
if (a->x) {
- TCGv_i64 tmp = tcg_temp_new_i64();
- tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3);
- tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8);
+ DisasIAQE next = iaqe_incr(&ctx->iaq_f, 8);
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
/* The computation here never changes privilege level. */
- return do_ibranch(ctx, tmp, a->l, a->n);
+ copy_iaoq_entry(ctx, t0, &next);
+ tcg_gen_shli_i64(t1, load_gpr(ctx, a->x), 3);
+ tcg_gen_add_i64(t0, t0, t1);
+
+ ctx->iaq_j = iaqe_next_absv(ctx, t0);
+ return do_ibranch(ctx, a->l, false, a->n);
} else {
/* BLR R0,RX is a good way to load PC+8 into RX. */
- return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n);
+ return do_dbranch(ctx, 0, a->l, a->n);
}
}
@@ -4049,34 +4051,22 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a)
tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b));
}
dest = do_ibranch_priv(ctx, dest);
- return do_ibranch(ctx, dest, 0, a->n);
+ ctx->iaq_j = iaqe_next_absv(ctx, dest);
+
+ return do_ibranch(ctx, 0, false, a->n);
}
static bool trans_bve(DisasContext *ctx, arg_bve *a)
{
- TCGv_i64 dest;
+ TCGv_i64 b = load_gpr(ctx, a->b);
-#ifdef CONFIG_USER_ONLY
- dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b));
- return do_ibranch(ctx, dest, a->l, a->n);
-#else
- nullify_over(ctx);
- dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b));
-
- copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
- if (ctx->iaoq_b == -1) {
- tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
- }
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest);
- tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest));
- if (a->l) {
- copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var);
- }
- nullify_set(ctx, a->n);
- tcg_gen_lookup_and_goto_ptr();
- ctx->base.is_jmp = DISAS_NORETURN;
- return nullify_end(ctx);
+#ifndef CONFIG_USER_ONLY
+ ctx->iaq_j.space = space_select(ctx, 0, b);
#endif
+ ctx->iaq_j.base = do_ibranch_priv(ctx, b);
+ ctx->iaq_j.disp = 0;
+
+ return do_ibranch(ctx, a->l, false, a->n);
}
static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a)
@@ -4377,6 +4367,8 @@ static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a)
static bool trans_ftest(DisasContext *ctx, arg_ftest *a)
{
+ TCGCond tc = TCG_COND_TSTNE;
+ uint32_t mask;
TCGv_i64 t;
nullify_over(ctx);
@@ -4385,55 +4377,41 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a)
tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow));
if (a->y == 1) {
- int mask;
- bool inv = false;
-
switch (a->c) {
case 0: /* simple */
- tcg_gen_andi_i64(t, t, 0x4000000);
- ctx->null_cond = cond_make_0(TCG_COND_NE, t);
- goto done;
+ mask = R_FPSR_C_MASK;
+ break;
case 2: /* rej */
- inv = true;
+ tc = TCG_COND_TSTEQ;
/* fallthru */
case 1: /* acc */
- mask = 0x43ff800;
+ mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK;
break;
case 6: /* rej8 */
- inv = true;
+ tc = TCG_COND_TSTEQ;
/* fallthru */
case 5: /* acc8 */
- mask = 0x43f8000;
+ mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK;
break;
case 9: /* acc6 */
- mask = 0x43e0000;
+ mask = R_FPSR_C_MASK | R_FPSR_CQ0_4_MASK;
break;
case 13: /* acc4 */
- mask = 0x4380000;
+ mask = R_FPSR_C_MASK | R_FPSR_CQ0_2_MASK;
break;
case 17: /* acc2 */
- mask = 0x4200000;
+ mask = R_FPSR_C_MASK | R_FPSR_CQ0_MASK;
break;
default:
gen_illegal(ctx);
return true;
}
- if (inv) {
- TCGv_i64 c = tcg_constant_i64(mask);
- tcg_gen_or_i64(t, t, c);
- ctx->null_cond = cond_make(TCG_COND_EQ, t, c);
- } else {
- tcg_gen_andi_i64(t, t, mask);
- ctx->null_cond = cond_make_0(TCG_COND_EQ, t);
- }
} else {
unsigned cbit = (a->y ^ 1) - 1;
-
- tcg_gen_extract_i64(t, t, 21 - cbit, 1);
- ctx->null_cond = cond_make_0(TCG_COND_NE, t);
+ mask = R_FPSR_CA0_MASK >> cbit;
}
- done:
+ ctx->null_cond = cond_make_ti(tc, t, mask);
return nullify_end(ctx);
}
@@ -4639,34 +4617,38 @@ static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a)
static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
+ uint64_t cs_base;
int bound;
ctx->cs = cs;
ctx->tb_flags = ctx->base.tb->flags;
ctx->is_pa20 = hppa_is_pa20(cpu_env(cs));
+ ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B);
#ifdef CONFIG_USER_ONLY
- ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX);
+ ctx->privilege = PRIV_USER;
ctx->mmu_idx = MMU_USER_IDX;
- ctx->iaoq_f = ctx->base.pc_first | ctx->privilege;
- ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege;
ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN);
#else
ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3;
ctx->mmu_idx = (ctx->tb_flags & PSW_D
? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P)
: ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX);
+#endif
- /* Recover the IAOQ values from the GVA + PRIV. */
- uint64_t cs_base = ctx->base.tb->cs_base;
- uint64_t iasq_f = cs_base & ~0xffffffffull;
- int32_t diff = cs_base;
+ cs_base = ctx->base.tb->cs_base;
+ ctx->iaoq_first = ctx->base.pc_first + ctx->privilege;
- ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege;
- ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1);
-#endif
- ctx->iaoq_n = -1;
- ctx->iaoq_n_var = NULL;
+ if (unlikely(cs_base & CS_BASE_DIFFSPACE)) {
+ ctx->iaq_b.space = cpu_iasq_b;
+ ctx->iaq_b.base = cpu_iaoq_b;
+ } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) {
+ ctx->iaq_b.base = cpu_iaoq_b;
+ } else {
+ uint64_t iaoq_f_pgofs = ctx->iaoq_first & ~TARGET_PAGE_MASK;
+ uint64_t iaoq_b_pgofs = cs_base & ~TARGET_PAGE_MASK;
+ ctx->iaq_b.disp = iaoq_b_pgofs - iaoq_f_pgofs;
+ }
ctx->zero = tcg_constant_i64(0);
@@ -4692,8 +4674,23 @@ static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
+ uint64_t iaoq_f, iaoq_b;
+ int64_t diff;
+
+ tcg_debug_assert(!iaqe_variable(&ctx->iaq_f));
+
+ iaoq_f = ctx->iaoq_first + ctx->iaq_f.disp;
+ if (iaqe_variable(&ctx->iaq_b)) {
+ diff = INT32_MIN;
+ } else {
+ iaoq_b = ctx->iaoq_first + ctx->iaq_b.disp;
+ diff = iaoq_b - iaoq_f;
+ /* Direct branches can only produce a 24-bit displacement. */
+ tcg_debug_assert(diff == (int32_t)diff);
+ tcg_debug_assert(diff != INT32_MIN);
+ }
- tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0);
+ tcg_gen_insn_start(iaoq_f & ~TARGET_PAGE_MASK, diff, 0);
ctx->insn_start_updated = false;
}
@@ -4716,16 +4713,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
the page permissions for execute. */
uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next);
- /* Set up the IA queue for the next insn.
- This will be overwritten by a branch. */
- if (ctx->iaoq_b == -1) {
- ctx->iaoq_n = -1;
- ctx->iaoq_n_var = tcg_temp_new_i64();
- tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4);
- } else {
- ctx->iaoq_n = ctx->iaoq_b + 4;
- ctx->iaoq_n_var = NULL;
- }
+ /*
+ * Set up the IA queue for the next insn.
+ * This will be overwritten by a branch.
+ */
+ ctx->iaq_n = NULL;
+ memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j));
+ ctx->psw_b_next = false;
if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) {
ctx->null_cond.c = TCG_COND_NEVER;
@@ -4738,51 +4732,47 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
ret = ctx->base.is_jmp;
assert(ctx->null_lab == NULL);
}
- }
- /* Advance the insn queue. Note that this check also detects
- a priority change within the instruction queue. */
- if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) {
- if (ctx->iaoq_b != -1 && ctx->iaoq_n != -1
- && use_goto_tb(ctx, ctx->iaoq_b)
- && (ctx->null_cond.c == TCG_COND_NEVER
- || ctx->null_cond.c == TCG_COND_ALWAYS)) {
- nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS);
- gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n);
- ctx->base.is_jmp = ret = DISAS_NORETURN;
- } else {
- ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE;
+ if (ret != DISAS_NORETURN) {
+ set_psw_xb(ctx, ctx->psw_b_next ? PSW_B : 0);
}
}
- ctx->iaoq_f = ctx->iaoq_b;
- ctx->iaoq_b = ctx->iaoq_n;
- ctx->base.pc_next += 4;
- switch (ret) {
- case DISAS_NORETURN:
- case DISAS_IAQ_N_UPDATED:
- break;
-
- case DISAS_NEXT:
- case DISAS_IAQ_N_STALE:
- case DISAS_IAQ_N_STALE_EXIT:
- if (ctx->iaoq_f == -1) {
- copy_iaoq_entry(ctx, cpu_iaoq_f, -1, cpu_iaoq_b);
- copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
-#ifndef CONFIG_USER_ONLY
- tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
-#endif
- nullify_save(ctx);
- ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT
- ? DISAS_EXIT
- : DISAS_IAQ_N_UPDATED);
- } else if (ctx->iaoq_b == -1) {
- copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var);
- }
- break;
+ /* If the TranslationBlock must end, do so. */
+ ctx->base.pc_next += 4;
+ if (ret != DISAS_NEXT) {
+ return;
+ }
+ /* Note this also detects a priority change. */
+ if (iaqe_variable(&ctx->iaq_b)
+ || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) {
+ ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+ return;
+ }
- default:
- g_assert_not_reached();
+ /*
+ * Advance the insn queue.
+ * The only exit now is DISAS_TOO_MANY from the translator loop.
+ */
+ ctx->iaq_f.disp = ctx->iaq_b.disp;
+ if (!ctx->iaq_n) {
+ ctx->iaq_b.disp += 4;
+ return;
+ }
+ /*
+ * If IAQ_Next is variable in any way, we need to copy into the
+ * IAQ_Back globals, in case the next insn raises an exception.
+ */
+ if (ctx->iaq_n->base) {
+ copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaq_n);
+ ctx->iaq_b.base = cpu_iaoq_b;
+ ctx->iaq_b.disp = 0;
+ } else {
+ ctx->iaq_b.disp = ctx->iaq_n->disp;
+ }
+ if (ctx->iaq_n->space) {
+ tcg_gen_mov_i64(cpu_iasq_b, ctx->iaq_n->space);
+ ctx->iaq_b.space = cpu_iasq_b;
}
}
@@ -4790,56 +4780,82 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
DisasJumpType is_jmp = ctx->base.is_jmp;
+ /* Assume the insn queue has not been advanced. */
+ DisasIAQE *f = &ctx->iaq_b;
+ DisasIAQE *b = ctx->iaq_n;
switch (is_jmp) {
case DISAS_NORETURN:
break;
case DISAS_TOO_MANY:
+ /* The insn queue has not been advanced. */
+ f = &ctx->iaq_f;
+ b = &ctx->iaq_b;
+ /* FALLTHRU */
case DISAS_IAQ_N_STALE:
+ if (use_goto_tb(ctx, f, b)
+ && (ctx->null_cond.c == TCG_COND_NEVER
+ || ctx->null_cond.c == TCG_COND_ALWAYS)) {
+ nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS);
+ gen_goto_tb(ctx, 0, f, b);
+ break;
+ }
+ /* FALLTHRU */
case DISAS_IAQ_N_STALE_EXIT:
- copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f);
- copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b);
+ install_iaq_entries(ctx, f, b);
nullify_save(ctx);
- /* FALLTHRU */
- case DISAS_IAQ_N_UPDATED:
- if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
- tcg_gen_lookup_and_goto_ptr();
+ if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
+ tcg_gen_exit_tb(NULL, 0);
break;
}
/* FALLTHRU */
+ case DISAS_IAQ_N_UPDATED:
+ tcg_gen_lookup_and_goto_ptr();
+ break;
case DISAS_EXIT:
tcg_gen_exit_tb(NULL, 0);
break;
default:
g_assert_not_reached();
}
+
+ for (DisasDelayException *e = ctx->delay_excp_list; e ; e = e->next) {
+ gen_set_label(e->lab);
+ if (e->set_n >= 0) {
+ tcg_gen_movi_i64(cpu_psw_n, e->set_n);
+ }
+ if (e->set_iir) {
+ tcg_gen_st_i64(tcg_constant_i64(e->insn), tcg_env,
+ offsetof(CPUHPPAState, cr[CR_IIR]));
+ }
+ install_iaq_entries(ctx, &e->iaq_f, &e->iaq_b);
+ gen_excp_1(e->excp);
+ }
}
-static void hppa_tr_disas_log(const DisasContextBase *dcbase,
+#ifdef CONFIG_USER_ONLY
+static bool hppa_tr_disas_log(const DisasContextBase *dcbase,
CPUState *cs, FILE *logfile)
{
target_ulong pc = dcbase->pc_first;
-#ifdef CONFIG_USER_ONLY
switch (pc) {
case 0x00:
fprintf(logfile, "IN:\n0x00000000: (null)\n");
- return;
+ return true;
case 0xb0:
fprintf(logfile, "IN:\n0x000000b0: light-weight-syscall\n");
- return;
+ return true;
case 0xe0:
fprintf(logfile, "IN:\n0x000000e0: set-thread-pointer-syscall\n");
- return;
+ return true;
case 0x100:
fprintf(logfile, "IN:\n0x00000100: syscall\n");
- return;
+ return true;
}
-#endif
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(pc));
- target_disas(logfile, cs, pc, dcbase->tb->size);
+ return false;
}
+#endif
static const TranslatorOps hppa_tr_ops = {
.init_disas_context = hppa_tr_init_disas_context,
@@ -4847,12 +4863,14 @@ static const TranslatorOps hppa_tr_ops = {
.insn_start = hppa_tr_insn_start,
.translate_insn = hppa_tr_translate_insn,
.tb_stop = hppa_tr_tb_stop,
+#ifdef CONFIG_USER_ONLY
.disas_log = hppa_tr_disas_log,
+#endif
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
vaddr pc, void *host_pc)
{
- DisasContext ctx;
+ DisasContext ctx = { };
translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base);
}
diff --git a/target/i386/Kconfig b/target/i386/Kconfig
index ce6968906e..6b0feef029 100644
--- a/target/i386/Kconfig
+++ b/target/i386/Kconfig
@@ -1,5 +1,9 @@
config I386
bool
+ select APIC
+ # kvm_arch_fixup_msi_route() needs to access PCIDevice
+ select PCI if KVM
config X86_64
bool
+ select I386
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index fa1ea3735d..cfe7c92d6b 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -398,12 +398,9 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info,
* 31:11 Reserved.
* 10:8 NodesPerProcessor: Node per processor. Read-only. Reset: XXXb.
* ValidValues:
- * Value Description
- * 000b 1 node per processor.
- * 001b 2 nodes per processor.
- * 010b Reserved.
- * 011b 4 nodes per processor.
- * 111b-100b Reserved.
+ * Value Description
+ * 0h 1 node per processor.
+ * 7h-1h Reserved.
* 7:0 NodeId: Node ID. Read-only. Reset: XXh.
*
* NOTE: Hardware reserves 3 bits for number of nodes per processor.
@@ -412,8 +409,12 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info,
* NodeId is combination of node and socket_id which is already decoded
* in apic_id. Just use it by shifting.
*/
- *ecx = ((topo_info->dies_per_pkg - 1) << 8) |
- ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF);
+ if (cpu->legacy_multi_node) {
+ *ecx = ((topo_info->dies_per_pkg - 1) << 8) |
+ ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF);
+ } else {
+ *ecx = (cpu->apic_id >> apicid_pkg_offset(topo_info)) & 0xFF;
+ }
*edx = 0;
}
@@ -712,7 +713,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
#endif
#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \
CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \
- CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \
+ CPUID_7_0_EBX_CLFLUSHOPT | \
CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \
CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_RDSEED | \
CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_KERNEL_FEATURES)
@@ -1550,8 +1551,8 @@ static FeatureDep feature_dependencies[] = {
.to = { FEAT_SVM, ~0ull },
},
{
- .from = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE },
- .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG },
+ .from = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG },
+ .to = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE },
},
};
@@ -3970,6 +3971,17 @@ static const X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
}
},
+ {
+ .version = 3,
+ .props = (PropValue[]) {
+ { "ss", "on" },
+ { "tsc-adjust", "on" },
+ { "cldemote", "on" },
+ { "movdiri", "on" },
+ { "movdir64b", "on" },
+ { /* end of list */ }
+ }
+ },
{ /* end of list */ }
}
},
@@ -5708,7 +5720,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data)
desc = g_strdup_printf("%s (deprecated)", olddesc);
}
- qemu_printf("x86 %-20s %s\n", name, desc);
+ qemu_printf(" %-20s %s\n", name, desc);
}
/* list available CPU models and flags */
@@ -7371,7 +7383,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
/* Use pc-relative instructions in system-mode */
- cs->tcg_cflags |= CF_PCREL;
+ tcg_cflags_set(cs, CF_PCREL);
#endif
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
@@ -8073,6 +8085,7 @@ static Property x86_cpu_properties[] = {
* own cache information (see x86_cpu_load_def()).
*/
DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true),
+ DEFINE_PROP_BOOL("legacy-multi-node", X86CPU, legacy_multi_node, false),
DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false),
/*
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 565c7a98c3..ccccb62fc3 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -816,8 +816,6 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_7_0_EBX_SMAP (1U << 20)
/* AVX-512 Integer Fused Multiply Add */
#define CPUID_7_0_EBX_AVX512IFMA (1U << 21)
-/* Persistent Commit */
-#define CPUID_7_0_EBX_PCOMMIT (1U << 22)
/* Flush a Cache Line Optimized */
#define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23)
/* Cache Line Write Back */
@@ -1994,6 +1992,12 @@ struct ArchCPU {
*/
bool legacy_cache;
+ /* Compatibility bits for old machine types.
+ * If true decode the CPUID Function 0x8000001E_ECX to support multiple
+ * nodes per processor
+ */
+ bool legacy_multi_node;
+
/* Compatibility bits for old machine types: */
bool enable_cpuid_0xb;
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index ebb000df6a..4acf485879 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -19,7 +19,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
-#include "include/gdbstub/helpers.h"
+#include "gdbstub/helpers.h"
#ifdef TARGET_X86_64
static const int gpr_map[16] = {
diff --git a/target/i386/helper.c b/target/i386/helper.c
index 23ccb23a5b..48d1513a35 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -523,7 +523,7 @@ static inline target_ulong get_memio_eip(CPUX86State *env)
}
/* Per x86_restore_state_to_opc. */
- if (cs->tcg_cflags & CF_PCREL) {
+ if (tcg_cflags_has(cs, CF_PCREL)) {
return (env->eip & TARGET_PAGE_MASK) | data[0];
} else {
return data[0] - env->segs[R_CS].base;
diff --git a/target/i386/helper.h b/target/i386/helper.h
index ac2b04abd6..3c207ac62d 100644
--- a/target/i386/helper.h
+++ b/target/i386/helper.h
@@ -207,15 +207,4 @@ DEF_HELPER_1(emms, void, env)
#define SHIFT 2
#include "tcg/ops_sse_header.h.inc"
-DEF_HELPER_3(rclb, tl, env, tl, tl)
-DEF_HELPER_3(rclw, tl, env, tl, tl)
-DEF_HELPER_3(rcll, tl, env, tl, tl)
-DEF_HELPER_3(rcrb, tl, env, tl, tl)
-DEF_HELPER_3(rcrw, tl, env, tl, tl)
-DEF_HELPER_3(rcrl, tl, env, tl, tl)
-#ifdef TARGET_X86_64
-DEF_HELPER_3(rclq, tl, env, tl, tl)
-DEF_HELPER_3(rcrq, tl, env, tl, tl)
-#endif
-
DEF_HELPER_1(rdrand, tl, env)
diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c
index f9cced53b3..65768aca03 100644
--- a/target/i386/nvmm/nvmm-all.c
+++ b/target/i386/nvmm/nvmm-all.c
@@ -982,7 +982,7 @@ nvmm_init_vcpu(CPUState *cpu)
}
}
- cpu->accel->dirty = true;
+ qcpu->dirty = true;
cpu->accel = qcpu;
return 0;
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index 426c459412..141ab2bc56 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -33,6 +33,32 @@
* ("cannot encode 16-bit or 32-bit size in 64-bit mode") as modifiers of the
* "v" or "z" sizes. The decoder simply makes them separate operand sizes.
*
+ * The manual lists immediate far destinations as Ap (technically an implicit
+ * argument). The decoder splits them into two immediates, using "Ip" for
+ * the offset part (that comes first in the instruction stream) and "Iw" for
+ * the segment/selector part. The size of the offset is given by s->dflag
+ * and the instructions are illegal in 64-bit mode, so the choice of "Ip"
+ * is somewhat arbitrary; "Iv" or "Iz" would work just as well.
+ *
+ * Operand types
+ * -------------
+ *
+ * For memory-only operands, if the emitter functions wants to rely on
+ * generic load and writeback, the decoder needs to know the type of the
+ * operand. Therefore, M is often replaced by the more specific EM and WM
+ * (respectively selecting an ALU operand, like the operand type E, or a
+ * vector operand like the operand type W).
+ *
+ * Immediates are almost always signed or masked away in helpers. Two
+ * common exceptions are IN/OUT and absolute jumps. For these, there is
+ * an additional custom operand type "I_unsigned". Alternatively, the
+ * mask could be applied (and the original sign-extended value would be
+ * optimized away by TCG) in the emitter function.
+ *
+ * Finally, a "nop" operand type is used for multi-byte NOPs. It accepts
+ * any value of mod including 11b (unlike M) but it does not try to
+ * interpret the operand (like M).
+ *
* Vector operands
* ---------------
*
@@ -119,8 +145,12 @@
## __VA_ARGS__ \
}
+#define X86_OP_GROUP1(op, op0, s0, ...) \
+ X86_OP_GROUP3(op, op0, s0, 2op, s0, None, None, ## __VA_ARGS__)
#define X86_OP_GROUP2(op, op0, s0, op1, s1, ...) \
X86_OP_GROUP3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__)
+#define X86_OP_GROUPw(op, op0, s0, ...) \
+ X86_OP_GROUP3(op, op0, s0, None, None, None, None, ## __VA_ARGS__)
#define X86_OP_GROUP0(op, ...) \
X86_OP_GROUP3(op, None, None, None, None, None, None, ## __VA_ARGS__)
@@ -140,16 +170,30 @@
.op3 = X86_TYPE_I, .s3 = X86_SIZE_b, \
## __VA_ARGS__)
+/*
+ * Short forms that are mostly useful for ALU opcodes and other
+ * one-byte opcodes. For vector instructions it is usually
+ * clearer to write all three operands explicitly, because the
+ * corresponding gen_* function will use OP_PTRn rather than s->T0
+ * and s->T1.
+ */
+#define X86_OP_ENTRYrr(op, op0, s0, op1, s1, ...) \
+ X86_OP_ENTRY3(op, None, None, op0, s0, op1, s1, ## __VA_ARGS__)
+#define X86_OP_ENTRYwr(op, op0, s0, op1, s1, ...) \
+ X86_OP_ENTRY3(op, op0, s0, None, None, op1, s1, ## __VA_ARGS__)
#define X86_OP_ENTRY2(op, op0, s0, op1, s1, ...) \
X86_OP_ENTRY3(op, op0, s0, 2op, s0, op1, s1, ## __VA_ARGS__)
#define X86_OP_ENTRYw(op, op0, s0, ...) \
X86_OP_ENTRY3(op, op0, s0, None, None, None, None, ## __VA_ARGS__)
#define X86_OP_ENTRYr(op, op0, s0, ...) \
X86_OP_ENTRY3(op, None, None, None, None, op0, s0, ## __VA_ARGS__)
+#define X86_OP_ENTRY1(op, op0, s0, ...) \
+ X86_OP_ENTRY3(op, op0, s0, 2op, s0, None, None, ## __VA_ARGS__)
#define X86_OP_ENTRY0(op, ...) \
X86_OP_ENTRY3(op, None, None, None, None, None, None, ## __VA_ARGS__)
#define cpuid(feat) .cpuid = X86_FEAT_##feat,
+#define noseg .special = X86_SPECIAL_NoSeg,
#define xchg .special = X86_SPECIAL_Locked,
#define lock .special = X86_SPECIAL_HasLock,
#define mmx .special = X86_SPECIAL_MMX,
@@ -196,6 +240,8 @@
#define p_66_f3_f2 .valid_prefix = P_66 | P_F3 | P_F2,
#define p_00_66_f3_f2 .valid_prefix = P_00 | P_66 | P_F3 | P_F2,
+#define UNKNOWN_OPCODE ((X86OpEntry) {})
+
static uint8_t get_modrm(DisasContext *s, CPUX86State *env)
{
if (!s->has_modrm) {
@@ -957,6 +1003,15 @@ static const X86OpEntry opcodes_0F[256] = {
/* Incorrectly listed as Mq,Vq in the manual */
[0x17] = X86_OP_ENTRY3(VMOVHPx_st, M,q, None,None, V,dq, vex5 p_00_66),
+ [0x40] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x41] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x42] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x43] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x44] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x45] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x46] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x47] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+
[0x50] = X86_OP_ENTRY3(MOVMSK, G,y, None,None, U,x, vex7 p_00_66),
[0x51] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), /* sqrtps */
[0x52] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex4_rep5 p_00_f3), /* rsqrtps */
@@ -984,6 +1039,37 @@ static const X86OpEntry opcodes_0F[256] = {
[0x76] = X86_OP_ENTRY3(PCMPEQD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
[0x77] = X86_OP_GROUP0(0F77),
+ [0x80] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x81] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x82] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x83] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x84] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x85] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x86] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x87] = X86_OP_ENTRYr(Jcc, J,z_f64),
+
+ [0x90] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x91] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x92] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x93] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x94] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x95] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x96] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x97] = X86_OP_ENTRYw(SETcc, E,b),
+
+ [0xa0] = X86_OP_ENTRYr(PUSH, FS, w),
+ [0xa1] = X86_OP_ENTRYw(POP, FS, w),
+
+ [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */
+ [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */
+
+ [0x18] = X86_OP_ENTRY1(NOP, nop,v), /* prefetch/reserved NOP */
+ [0x19] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */
+ [0x1c] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */
+ [0x1d] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */
+ [0x1e] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */
+ [0x1f] = X86_OP_ENTRY1(NOP, nop,v), /* NOP/reserved NOP */
+
[0x28] = X86_OP_ENTRY3(MOVDQ, V,x, None,None, W,x, vex1 p_00_66), /* MOVAPS */
[0x29] = X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex1 p_00_66), /* MOVAPS */
[0x2A] = X86_OP_GROUP0(0F2A),
@@ -996,6 +1082,15 @@ static const X86OpEntry opcodes_0F[256] = {
[0x38] = X86_OP_GROUP0(0F38),
[0x3a] = X86_OP_GROUP0(0F3A),
+ [0x48] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x49] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4a] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4b] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4c] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4d] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4e] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+ [0x4f] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)),
+
[0x58] = X86_OP_ENTRY3(VADD, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2),
[0x59] = X86_OP_ENTRY3(VMUL, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2),
[0x5a] = X86_OP_GROUP0(0F5A),
@@ -1021,13 +1116,59 @@ static const X86OpEntry opcodes_0F[256] = {
[0x7e] = X86_OP_GROUP0(0F7E),
[0x7f] = X86_OP_GROUP0(0F7F),
+ [0x88] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x89] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8a] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8b] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8c] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8d] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8e] = X86_OP_ENTRYr(Jcc, J,z_f64),
+ [0x8f] = X86_OP_ENTRYr(Jcc, J,z_f64),
+
+ [0x98] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x99] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9a] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9b] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9c] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9d] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9e] = X86_OP_ENTRYw(SETcc, E,b),
+ [0x9f] = X86_OP_ENTRYw(SETcc, E,b),
+
+ [0xa8] = X86_OP_ENTRYr(PUSH, GS, w),
+ [0xa9] = X86_OP_ENTRYw(POP, GS, w),
[0xae] = X86_OP_GROUP0(group15),
+ /*
+ * It's slightly more efficient to put Ev operand in T0 and allow gen_IMUL3
+ * to assume sextT0. Multiplication is commutative anyway.
+ */
+ [0xaf] = X86_OP_ENTRY3(IMUL3, G,v, E,v, 2op,v, sextT0),
+
+ [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None),
+ [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None),
+ [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None),
+ [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */
+ [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */
+
+ /* decoded as modrm, which is visible as a difference between page fault and #UD */
+ [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */
+ [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */
+ [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */
[0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2),
+ [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */
[0xc4] = X86_OP_ENTRY4(PINSRW, V,dq,H,dq,E,w, vex5 mmx p_00_66),
[0xc5] = X86_OP_ENTRY3(PEXTRW, G,d, U,dq,I,b, vex5 mmx p_00_66),
[0xc6] = X86_OP_ENTRY4(VSHUF, V,x, H,x, W,x, vex4 p_00_66),
+ [0xc8] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xc9] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xca] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xcb] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xcc] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xcd] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xce] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+ [0xcf] = X86_OP_ENTRY1(BSWAP, LoBits,y),
+
[0xd0] = X86_OP_ENTRY3(VADDSUB, V,x, H,x, W,x, vex2 cpuid(SSE3) p_66_f2),
[0xd1] = X86_OP_ENTRY3(PSRLW_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
[0xd2] = X86_OP_ENTRY3(PSRLD_r, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
@@ -1081,7 +1222,7 @@ static const X86OpEntry opcodes_0F[256] = {
[0xfc] = X86_OP_ENTRY3(PADDB, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
[0xfd] = X86_OP_ENTRY3(PADDW, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
[0xfe] = X86_OP_ENTRY3(PADDD, V,x, H,x, W,x, vex4 mmx avx2_256 p_00_66),
- /* 0xff = UD0 */
+ [0xff] = X86_OP_ENTRYr(UD, nop,v), /* UD0 */
};
static void do_decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
@@ -1095,8 +1236,405 @@ static void decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint
do_decode_0F(s, env, entry, b);
}
+static void decode_63(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ static const X86OpEntry arpl = X86_OP_ENTRY2(ARPL, E,w, G,w, chk(prot));
+ static const X86OpEntry mov = X86_OP_ENTRY3(MOV, G,v, E,v, None, None);
+ static const X86OpEntry movsxd = X86_OP_ENTRY3(MOV, G,v, E,d, None, None, sextT0);
+ if (!CODE64(s)) {
+ *entry = arpl;
+ } else if (REX_W(s)) {
+ *entry = movsxd;
+ } else {
+ *entry = mov;
+ }
+}
+
+static void decode_group1(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ static const X86GenFunc group1_gen[8] = {
+ gen_ADD, gen_OR, gen_ADC, gen_SBB, gen_AND, gen_SUB, gen_XOR, gen_SUB,
+ };
+ int op = (get_modrm(s, env) >> 3) & 7;
+ entry->gen = group1_gen[op];
+
+ if (op == 7) {
+ /* prevent writeback for CMP */
+ entry->op1 = entry->op0;
+ entry->op0 = X86_TYPE_None;
+ entry->s0 = X86_SIZE_None;
+ } else {
+ entry->special = X86_SPECIAL_HasLock;
+ }
+}
+
+static void decode_group1A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ int op = (get_modrm(s, env) >> 3) & 7;
+ if (op != 0) {
+ /* could be XOP prefix too */
+ *entry = UNKNOWN_OPCODE;
+ } else {
+ entry->gen = gen_POP;
+ /* The address must use the value of ESP after the pop. */
+ s->popl_esp_hack = 1 << mo_pushpop(s, s->dflag);
+ }
+}
+
+static void decode_group2(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ static const X86GenFunc group2_gen[8] = {
+ gen_ROL, gen_ROR, gen_RCL, gen_RCR,
+ gen_SHL, gen_SHR, gen_SHL /* SAL, undocumented */, gen_SAR,
+ };
+ int op = (get_modrm(s, env) >> 3) & 7;
+ entry->gen = group2_gen[op];
+ if (op == 7) {
+ entry->special = X86_SPECIAL_SExtT0;
+ } else {
+ entry->special = X86_SPECIAL_ZExtT0;
+ }
+}
+
+static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ static const X86OpEntry opcodes_grp3[16] = {
+ /* 0xf6 */
+ [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b),
+ [0x02] = X86_OP_ENTRY1(NOT, E,b, lock),
+ [0x03] = X86_OP_ENTRY1(NEG, E,b, lock),
+ [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0),
+ [0x05] = X86_OP_ENTRYrr(IMUL,E,b, 0,b, sextT0),
+ [0x06] = X86_OP_ENTRYr(DIV, E,b),
+ [0x07] = X86_OP_ENTRYr(IDIV, E,b),
+
+ /* 0xf7 */
+ [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z),
+ [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock),
+ [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock),
+ [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0),
+ [0x0d] = X86_OP_ENTRYrr(IMUL,E,v, 0,v, sextT0),
+ [0x0e] = X86_OP_ENTRYr(DIV, E,v),
+ [0x0f] = X86_OP_ENTRYr(IDIV, E,v),
+ };
+
+ int w = (*b & 1);
+ int reg = (get_modrm(s, env) >> 3) & 7;
+
+ *entry = opcodes_grp3[(w << 3) | reg];
+}
+
+static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ static const X86OpEntry opcodes_grp4_5[16] = {
+ /* 0xfe */
+ [0x00] = X86_OP_ENTRY1(INC, E,b, lock),
+ [0x01] = X86_OP_ENTRY1(DEC, E,b, lock),
+
+ /* 0xff */
+ [0x08] = X86_OP_ENTRY1(INC, E,v, lock),
+ [0x09] = X86_OP_ENTRY1(DEC, E,v, lock),
+ [0x0a] = X86_OP_ENTRY3(CALL_m, None, None, E,f64, None, None, zextT0),
+ [0x0b] = X86_OP_ENTRYr(CALLF_m, M,p),
+ [0x0c] = X86_OP_ENTRY3(JMP_m, None, None, E,f64, None, None, zextT0),
+ [0x0d] = X86_OP_ENTRYr(JMPF_m, M,p),
+ [0x0e] = X86_OP_ENTRYr(PUSH, E,f64),
+ };
+
+ int w = (*b & 1);
+ int reg = (get_modrm(s, env) >> 3) & 7;
+
+ *entry = opcodes_grp4_5[(w << 3) | reg];
+}
+
+
+static void decode_group11(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b)
+{
+ int op = (get_modrm(s, env) >> 3) & 7;
+ if (op != 0) {
+ *entry = UNKNOWN_OPCODE;
+ } else {
+ entry->gen = gen_MOV;
+ }
+}
+
static const X86OpEntry opcodes_root[256] = {
+ [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock),
+ [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock),
+ [0x02] = X86_OP_ENTRY2(ADD, G,b, E,b, lock),
+ [0x03] = X86_OP_ENTRY2(ADD, G,v, E,v, lock),
+ [0x04] = X86_OP_ENTRY2(ADD, 0,b, I,b, lock), /* AL, Ib */
+ [0x05] = X86_OP_ENTRY2(ADD, 0,v, I,z, lock), /* rAX, Iz */
+ [0x06] = X86_OP_ENTRYr(PUSH, ES, w, chk(i64)),
+ [0x07] = X86_OP_ENTRYw(POP, ES, w, chk(i64)),
+
+ [0x10] = X86_OP_ENTRY2(ADC, E,b, G,b, lock),
+ [0x11] = X86_OP_ENTRY2(ADC, E,v, G,v, lock),
+ [0x12] = X86_OP_ENTRY2(ADC, G,b, E,b, lock),
+ [0x13] = X86_OP_ENTRY2(ADC, G,v, E,v, lock),
+ [0x14] = X86_OP_ENTRY2(ADC, 0,b, I,b, lock), /* AL, Ib */
+ [0x15] = X86_OP_ENTRY2(ADC, 0,v, I,z, lock), /* rAX, Iz */
+ [0x16] = X86_OP_ENTRYr(PUSH, SS, w, chk(i64)),
+ [0x17] = X86_OP_ENTRYw(POP, SS, w, chk(i64)),
+
+ [0x20] = X86_OP_ENTRY2(AND, E,b, G,b, lock),
+ [0x21] = X86_OP_ENTRY2(AND, E,v, G,v, lock),
+ [0x22] = X86_OP_ENTRY2(AND, G,b, E,b, lock),
+ [0x23] = X86_OP_ENTRY2(AND, G,v, E,v, lock),
+ [0x24] = X86_OP_ENTRY2(AND, 0,b, I,b, lock), /* AL, Ib */
+ [0x25] = X86_OP_ENTRY2(AND, 0,v, I,z, lock), /* rAX, Iz */
+ [0x26] = {},
+ [0x27] = X86_OP_ENTRY0(DAA, chk(i64)),
+
+ [0x30] = X86_OP_ENTRY2(XOR, E,b, G,b, lock),
+ [0x31] = X86_OP_ENTRY2(XOR, E,v, G,v, lock),
+ [0x32] = X86_OP_ENTRY2(XOR, G,b, E,b, lock),
+ [0x33] = X86_OP_ENTRY2(XOR, G,v, E,v, lock),
+ [0x34] = X86_OP_ENTRY2(XOR, 0,b, I,b, lock), /* AL, Ib */
+ [0x35] = X86_OP_ENTRY2(XOR, 0,v, I,z, lock), /* rAX, Iz */
+ [0x36] = {},
+ [0x37] = X86_OP_ENTRY0(AAA, chk(i64)),
+
+ [0x40] = X86_OP_ENTRY1(INC, 0,v, chk(i64)),
+ [0x41] = X86_OP_ENTRY1(INC, 1,v, chk(i64)),
+ [0x42] = X86_OP_ENTRY1(INC, 2,v, chk(i64)),
+ [0x43] = X86_OP_ENTRY1(INC, 3,v, chk(i64)),
+ [0x44] = X86_OP_ENTRY1(INC, 4,v, chk(i64)),
+ [0x45] = X86_OP_ENTRY1(INC, 5,v, chk(i64)),
+ [0x46] = X86_OP_ENTRY1(INC, 6,v, chk(i64)),
+ [0x47] = X86_OP_ENTRY1(INC, 7,v, chk(i64)),
+
+ [0x50] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x51] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x52] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x53] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x54] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x55] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x56] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+ [0x57] = X86_OP_ENTRYr(PUSH, LoBits,d64),
+
+ [0x60] = X86_OP_ENTRY0(PUSHA, chk(i64)),
+ [0x61] = X86_OP_ENTRY0(POPA, chk(i64)),
+ [0x62] = X86_OP_ENTRYrr(BOUND, G,v, M,a, chk(i64)),
+ [0x63] = X86_OP_GROUP0(63),
+ [0x64] = {},
+ [0x65] = {},
+ [0x66] = {},
+ [0x67] = {},
+
+ [0x70] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x71] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x72] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x73] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x74] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x75] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x76] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x77] = X86_OP_ENTRYr(Jcc, J,b),
+
+ [0x80] = X86_OP_GROUP2(group1, E,b, I,b),
+ [0x81] = X86_OP_GROUP2(group1, E,v, I,z),
+ [0x82] = X86_OP_GROUP2(group1, E,b, I,b, chk(i64)),
+ [0x83] = X86_OP_GROUP2(group1, E,v, I,b),
+ [0x84] = X86_OP_ENTRYrr(AND, E,b, G,b),
+ [0x85] = X86_OP_ENTRYrr(AND, E,v, G,v),
+ [0x86] = X86_OP_ENTRY2(XCHG, E,b, G,b, xchg),
+ [0x87] = X86_OP_ENTRY2(XCHG, E,v, G,v, xchg),
+
+ [0x90] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x91] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x92] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x93] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x94] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x95] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x96] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+ [0x97] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v),
+
+ [0xA0] = X86_OP_ENTRY3(MOV, 0,b, O,b, None, None), /* AL, Ob */
+ [0xA1] = X86_OP_ENTRY3(MOV, 0,v, O,v, None, None), /* rAX, Ov */
+ [0xA2] = X86_OP_ENTRY3(MOV, O,b, 0,b, None, None), /* Ob, AL */
+ [0xA3] = X86_OP_ENTRY3(MOV, O,v, 0,v, None, None), /* Ov, rAX */
+ [0xA4] = X86_OP_ENTRYrr(MOVS, Y,b, X,b),
+ [0xA5] = X86_OP_ENTRYrr(MOVS, Y,v, X,v),
+ [0xA6] = X86_OP_ENTRYrr(CMPS, Y,b, X,b),
+ [0xA7] = X86_OP_ENTRYrr(CMPS, Y,v, X,v),
+
+ [0xB0] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB1] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB2] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB3] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB4] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB5] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB6] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+ [0xB7] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None),
+
+ [0xC0] = X86_OP_GROUP2(group2, E,b, I,b),
+ [0xC1] = X86_OP_GROUP2(group2, E,v, I,b),
+ [0xC2] = X86_OP_ENTRYr(RET, I,w),
+ [0xC3] = X86_OP_ENTRY0(RET),
+ [0xC4] = X86_OP_ENTRY3(LES, G,z, EM,p, None, None, chk(i64)),
+ [0xC5] = X86_OP_ENTRY3(LDS, G,z, EM,p, None, None, chk(i64)),
+ [0xC6] = X86_OP_GROUP3(group11, E,b, I,b, None, None), /* reg=000b */
+ [0xC7] = X86_OP_GROUP3(group11, E,v, I,z, None, None), /* reg=000b */
+
+ [0xD0] = X86_OP_GROUP1(group2, E,b),
+ [0xD1] = X86_OP_GROUP1(group2, E,v),
+ [0xD2] = X86_OP_GROUP2(group2, E,b, 1,b), /* CL */
+ [0xD3] = X86_OP_GROUP2(group2, E,v, 1,b), /* CL */
+ [0xD4] = X86_OP_ENTRYr(AAM, I,b),
+ [0xD5] = X86_OP_ENTRYr(AAD, I,b),
+ [0xD6] = X86_OP_ENTRYw(SALC, 0,b),
+ [0xD7] = X86_OP_ENTRY1(XLAT, 0,b, zextT0), /* AL read/written */
+
+ [0xE0] = X86_OP_ENTRYr(LOOPNE, J,b), /* implicit: CX with aflag size */
+ [0xE1] = X86_OP_ENTRYr(LOOPE, J,b), /* implicit: CX with aflag size */
+ [0xE2] = X86_OP_ENTRYr(LOOP, J,b), /* implicit: CX with aflag size */
+ [0xE3] = X86_OP_ENTRYr(JCXZ, J,b), /* implicit: CX with aflag size */
+ [0xE4] = X86_OP_ENTRYwr(IN, 0,b, I_unsigned,b), /* AL */
+ [0xE5] = X86_OP_ENTRYwr(IN, 0,v, I_unsigned,b), /* AX/EAX */
+ [0xE6] = X86_OP_ENTRYrr(OUT, 0,b, I_unsigned,b), /* AL */
+ [0xE7] = X86_OP_ENTRYrr(OUT, 0,v, I_unsigned,b), /* AX/EAX */
+
+ [0xF1] = X86_OP_ENTRY0(INT1, svm(ICEBP)),
+ [0xF4] = X86_OP_ENTRY0(HLT, chk(cpl0)),
+ [0xF5] = X86_OP_ENTRY0(CMC),
+ [0xF6] = X86_OP_GROUP1(group3, E,b),
+ [0xF7] = X86_OP_GROUP1(group3, E,v),
+
+ [0x08] = X86_OP_ENTRY2(OR, E,b, G,b, lock),
+ [0x09] = X86_OP_ENTRY2(OR, E,v, G,v, lock),
+ [0x0A] = X86_OP_ENTRY2(OR, G,b, E,b, lock),
+ [0x0B] = X86_OP_ENTRY2(OR, G,v, E,v, lock),
+ [0x0C] = X86_OP_ENTRY2(OR, 0,b, I,b, lock), /* AL, Ib */
+ [0x0D] = X86_OP_ENTRY2(OR, 0,v, I,z, lock), /* rAX, Iz */
+ [0x0E] = X86_OP_ENTRYr(PUSH, CS, w, chk(i64)),
[0x0F] = X86_OP_GROUP0(0F),
+
+ [0x18] = X86_OP_ENTRY2(SBB, E,b, G,b, lock),
+ [0x19] = X86_OP_ENTRY2(SBB, E,v, G,v, lock),
+ [0x1A] = X86_OP_ENTRY2(SBB, G,b, E,b, lock),
+ [0x1B] = X86_OP_ENTRY2(SBB, G,v, E,v, lock),
+ [0x1C] = X86_OP_ENTRY2(SBB, 0,b, I,b, lock), /* AL, Ib */
+ [0x1D] = X86_OP_ENTRY2(SBB, 0,v, I,z, lock), /* rAX, Iz */
+ [0x1E] = X86_OP_ENTRYr(PUSH, DS, w, chk(i64)),
+ [0x1F] = X86_OP_ENTRYw(POP, DS, w, chk(i64)),
+
+ [0x28] = X86_OP_ENTRY2(SUB, E,b, G,b, lock),
+ [0x29] = X86_OP_ENTRY2(SUB, E,v, G,v, lock),
+ [0x2A] = X86_OP_ENTRY2(SUB, G,b, E,b, lock),
+ [0x2B] = X86_OP_ENTRY2(SUB, G,v, E,v, lock),
+ [0x2C] = X86_OP_ENTRY2(SUB, 0,b, I,b, lock), /* AL, Ib */
+ [0x2D] = X86_OP_ENTRY2(SUB, 0,v, I,z, lock), /* rAX, Iz */
+ [0x2E] = {},
+ [0x2F] = X86_OP_ENTRY0(DAS, chk(i64)),
+
+ [0x38] = X86_OP_ENTRYrr(SUB, E,b, G,b),
+ [0x39] = X86_OP_ENTRYrr(SUB, E,v, G,v),
+ [0x3A] = X86_OP_ENTRYrr(SUB, G,b, E,b),
+ [0x3B] = X86_OP_ENTRYrr(SUB, G,v, E,v),
+ [0x3C] = X86_OP_ENTRYrr(SUB, 0,b, I,b), /* AL, Ib */
+ [0x3D] = X86_OP_ENTRYrr(SUB, 0,v, I,z), /* rAX, Iz */
+ [0x3E] = {},
+ [0x3F] = X86_OP_ENTRY0(AAS, chk(i64)),
+
+ [0x48] = X86_OP_ENTRY1(DEC, 0,v, chk(i64)),
+ [0x49] = X86_OP_ENTRY1(DEC, 1,v, chk(i64)),
+ [0x4A] = X86_OP_ENTRY1(DEC, 2,v, chk(i64)),
+ [0x4B] = X86_OP_ENTRY1(DEC, 3,v, chk(i64)),
+ [0x4C] = X86_OP_ENTRY1(DEC, 4,v, chk(i64)),
+ [0x4D] = X86_OP_ENTRY1(DEC, 5,v, chk(i64)),
+ [0x4E] = X86_OP_ENTRY1(DEC, 6,v, chk(i64)),
+ [0x4F] = X86_OP_ENTRY1(DEC, 7,v, chk(i64)),
+
+ [0x58] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x59] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5A] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5B] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5C] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5D] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5E] = X86_OP_ENTRYw(POP, LoBits,d64),
+ [0x5F] = X86_OP_ENTRYw(POP, LoBits,d64),
+
+ [0x68] = X86_OP_ENTRYr(PUSH, I,z),
+ [0x69] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,z, sextT0),
+ [0x6A] = X86_OP_ENTRYr(PUSH, I,b),
+ [0x6B] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,b, sextT0),
+ [0x6C] = X86_OP_ENTRYrr(INS, Y,b, 2,w), /* DX */
+ [0x6D] = X86_OP_ENTRYrr(INS, Y,z, 2,w), /* DX */
+ [0x6E] = X86_OP_ENTRYrr(OUTS, X,b, 2,w), /* DX */
+ [0x6F] = X86_OP_ENTRYrr(OUTS, X,z, 2,w), /* DX */
+
+ [0x78] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x79] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7A] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7B] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7C] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7D] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7E] = X86_OP_ENTRYr(Jcc, J,b),
+ [0x7F] = X86_OP_ENTRYr(Jcc, J,b),
+
+ [0x88] = X86_OP_ENTRY3(MOV, E,b, G,b, None, None),
+ [0x89] = X86_OP_ENTRY3(MOV, E,v, G,v, None, None),
+ [0x8A] = X86_OP_ENTRY3(MOV, G,b, E,b, None, None),
+ [0x8B] = X86_OP_ENTRY3(MOV, G,v, E,v, None, None),
+ [0x8C] = X86_OP_ENTRY3(MOV, E,v, S,w, None, None),
+ [0x8D] = X86_OP_ENTRY3(LEA, G,v, M,v, None, None, noseg),
+ [0x8E] = X86_OP_ENTRY3(MOV, S,w, E,v, None, None),
+ [0x8F] = X86_OP_GROUPw(group1A, E,v),
+
+ [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */
+ [0x99] = X86_OP_ENTRY3(CWD, 2,v, 0,v, None, None), /* rDX, rAX */
+ [0x9A] = X86_OP_ENTRYrr(CALLF, I_unsigned,p, I_unsigned,w, chk(i64)),
+ [0x9B] = X86_OP_ENTRY0(WAIT),
+ [0x9C] = X86_OP_ENTRY0(PUSHF, chk(vm86_iopl) svm(PUSHF)),
+ [0x9D] = X86_OP_ENTRY0(POPF, chk(vm86_iopl) svm(POPF)),
+ [0x9E] = X86_OP_ENTRY0(SAHF),
+ [0x9F] = X86_OP_ENTRY0(LAHF),
+
+ [0xA8] = X86_OP_ENTRYrr(AND, 0,b, I,b), /* AL, Ib */
+ [0xA9] = X86_OP_ENTRYrr(AND, 0,v, I,z), /* rAX, Iz */
+ [0xAA] = X86_OP_ENTRY3(STOS, Y,b, 0,b, None, None),
+ [0xAB] = X86_OP_ENTRY3(STOS, Y,v, 0,v, None, None),
+ /* Manual writeback because REP LODS (!) has to write EAX/RAX after every LODS. */
+ [0xAC] = X86_OP_ENTRYr(LODS, X,b),
+ [0xAD] = X86_OP_ENTRYr(LODS, X,v),
+ [0xAE] = X86_OP_ENTRYrr(SCAS, 0,b, Y,b),
+ [0xAF] = X86_OP_ENTRYrr(SCAS, 0,v, Y,v),
+
+ [0xB8] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xB9] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBA] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBB] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBC] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBD] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBE] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+ [0xBF] = X86_OP_ENTRY3(MOV, LoBits,v, I,v, None, None),
+
+ [0xC8] = X86_OP_ENTRYrr(ENTER, I,w, I,b),
+ [0xC9] = X86_OP_ENTRY1(LEAVE, A,d64),
+ [0xCA] = X86_OP_ENTRYr(RETF, I,w),
+ [0xCB] = X86_OP_ENTRY0(RETF),
+ [0xCC] = X86_OP_ENTRY0(INT3),
+ [0xCD] = X86_OP_ENTRYr(INT, I,b, chk(vm86_iopl)),
+ [0xCE] = X86_OP_ENTRY0(INTO),
+ [0xCF] = X86_OP_ENTRY0(IRET, chk(vm86_iopl) svm(IRET)),
+
+ [0xE8] = X86_OP_ENTRYr(CALL, J,z_f64),
+ [0xE9] = X86_OP_ENTRYr(JMP, J,z_f64),
+ [0xEA] = X86_OP_ENTRYrr(JMPF, I_unsigned,p, I_unsigned,w, chk(i64)),
+ [0xEB] = X86_OP_ENTRYr(JMP, J,b),
+ [0xEC] = X86_OP_ENTRYwr(IN, 0,b, 2,w), /* AL, DX */
+ [0xED] = X86_OP_ENTRYwr(IN, 0,v, 2,w), /* AX/EAX, DX */
+ [0xEE] = X86_OP_ENTRYrr(OUT, 0,b, 2,w), /* DX, AL */
+ [0xEF] = X86_OP_ENTRYrr(OUT, 0,v, 2,w), /* DX, AX/EAX */
+
+ [0xF8] = X86_OP_ENTRY0(CLC),
+ [0xF9] = X86_OP_ENTRY0(STC),
+ [0xFA] = X86_OP_ENTRY0(CLI, chk(iopl)),
+ [0xFB] = X86_OP_ENTRY0(STI, chk(iopl)),
+ [0xFC] = X86_OP_ENTRY0(CLD),
+ [0xFD] = X86_OP_ENTRY0(STD),
+ [0xFE] = X86_OP_GROUP1(group4_5, E,b),
+ [0xFF] = X86_OP_GROUP1(group4_5, E,v),
};
#undef mmx
@@ -1176,6 +1714,10 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp
*ot = s->dflag == MO_16 ? MO_16 : MO_32;
return true;
+ case X86_SIZE_z_f64: /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */
+ *ot = !CODE64(s) && s->dflag == MO_16 ? MO_16 : MO_32;
+ return true;
+
case X86_SIZE_dq: /* SSE/AVX 128-bit */
if (e->special == X86_SPECIAL_MMX &&
!(s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) {
@@ -1315,12 +1857,19 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
case X86_TYPE_WM: /* modrm byte selects an XMM/YMM memory operand */
op->unit = X86_OP_SSE;
+ goto get_modrm_mem;
+
+ case X86_TYPE_EM: /* modrm byte selects an ALU memory operand */
+ op->unit = X86_OP_INT;
/* fall through */
case X86_TYPE_M: /* modrm byte selects a memory operand */
+ get_modrm_mem:
modrm = get_modrm(s, env);
if ((modrm >> 6) == 3) {
return false;
}
+ /* fall through */
+ case X86_TYPE_nop: /* modrm operand decoded but not fetched */
get_modrm:
decode_modrm(s, env, decode, op, type);
break;
@@ -1353,7 +1902,12 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
case X86_TYPE_I: /* Immediate */
case X86_TYPE_J: /* Relative offset for a jump */
op->unit = X86_OP_IMM;
- decode->immediate = insn_get_signed(env, s, op->ot);
+ decode->immediate = op->imm = insn_get_signed(env, s, op->ot);
+ break;
+
+ case X86_TYPE_I_unsigned: /* Immediate */
+ op->unit = X86_OP_IMM;
+ decode->immediate = op->imm = insn_get(env, s, op->ot);
break;
case X86_TYPE_L: /* The upper 4 bits of the immediate select a 128-bit register */
@@ -1476,6 +2030,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid)
switch (cpuid) {
case X86_FEAT_None:
return true;
+ case X86_FEAT_CMOV:
+ return (s->cpuid_features & CPUID_CMOV);
case X86_FEAT_F16C:
return (s->cpuid_ext_features & CPUID_EXT_F16C);
case X86_FEAT_FMA:
@@ -1681,22 +2237,31 @@ illegal:
* Convert one instruction. s->base.is_jmp is set if the translation must
* be stopped.
*/
-static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
+static void disas_insn(DisasContext *s, CPUState *cpu)
{
CPUX86State *env = cpu_env(cpu);
- bool first = true;
X86DecodedInsn decode;
X86DecodeFunc decode_func = decode_root;
- uint8_t cc_live;
+ uint8_t cc_live, b;
+ s->pc = s->base.pc_next;
+ s->override = -1;
+ s->popl_esp_hack = 0;
+#ifdef TARGET_X86_64
+ s->rex_r = 0;
+ s->rex_x = 0;
+ s->rex_b = 0;
+#endif
+ s->rip_offset = 0; /* for relative ip address */
+ s->vex_l = 0;
+ s->vex_v = 0;
+ s->vex_w = false;
s->has_modrm = false;
+ s->prefix = 0;
next_byte:
- if (first) {
- first = false;
- } else {
- b = x86_ldub_code(env, s);
- }
+ b = x86_ldub_code(env, s);
+
/* Collect prefixes. */
switch (b) {
case 0xf3:
@@ -1808,10 +2373,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
}
break;
default:
- if (b >= 0x100) {
- b -= 0x100;
- decode_func = do_decode_0F;
- }
break;
}
@@ -1840,6 +2401,40 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
}
}
+ /* Go back to old decoder for unconverted opcodes. */
+ if (!(s->prefix & PREFIX_VEX)) {
+ if ((b & ~7) == 0xd8) {
+ if (!disas_insn_x87(s, cpu, b)) {
+ goto unknown_op;
+ }
+ return;
+ }
+
+ if (b == 0x0f) {
+ b = x86_ldub_code(env, s);
+ switch (b) {
+ case 0x00 ... 0x03: /* mostly privileged instructions */
+ case 0x05 ... 0x09:
+ case 0x1a ... 0x1b: /* MPX */
+ case 0x20 ... 0x23: /* mov from/to CR and DR */
+ case 0x30 ... 0x35: /* more privileged instructions */
+ case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */
+ case 0xaa ... 0xae: /* RSM, SHRD, grp15 */
+ case 0xb0 ... 0xb1: /* cmpxchg */
+ case 0xb3: /* btr */
+ case 0xb8: /* integer ops */
+ case 0xba ... 0xbd: /* integer ops */
+ case 0xc0 ... 0xc1: /* xadd */
+ case 0xc7: /* grp9 */
+ disas_insn_old(s, cpu, b + 0x100);
+ return;
+ default:
+ decode_func = do_decode_0F;
+ break;
+ }
+ }
+ }
+
memset(&decode, 0, sizeof(decode));
decode.cc_op = -1;
decode.b = b;
@@ -1914,6 +2509,11 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
assert(decode.op[1].unit == X86_OP_INT);
break;
+ case X86_SPECIAL_NoSeg:
+ decode.mem.def_seg = -1;
+ s->override = -1;
+ break;
+
default:
break;
}
diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h
index 15e6bfef4b..51ef0e621b 100644
--- a/target/i386/tcg/decode-new.h
+++ b/target/i386/tcg/decode-new.h
@@ -47,7 +47,10 @@ typedef enum X86OpType {
X86_TYPE_Y, /* string destination */
/* Custom */
+ X86_TYPE_EM, /* modrm byte selects an ALU memory operand */
X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */
+ X86_TYPE_I_unsigned, /* Immediate, zero-extended */
+ X86_TYPE_nop, /* modrm operand decoded but not loaded into s->T{0,1} */
X86_TYPE_2op, /* 2-operand RMW instruction */
X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */
X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */
@@ -88,6 +91,7 @@ typedef enum X86OpSize {
X86_SIZE_x, /* 128/256-bit, based on operand size */
X86_SIZE_y, /* 32/64-bit, based on operand size */
X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */
+ X86_SIZE_z_f64, /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */
/* Custom */
X86_SIZE_d64,
@@ -104,6 +108,7 @@ typedef enum X86CPUIDFeature {
X86_FEAT_AVX2,
X86_FEAT_BMI1,
X86_FEAT_BMI2,
+ X86_FEAT_CMOV,
X86_FEAT_CMPCCXADD,
X86_FEAT_F16C,
X86_FEAT_FMA,
@@ -165,6 +170,8 @@ typedef enum X86InsnSpecial {
/* Always locked if it has a memory operand (XCHG) */
X86_SPECIAL_Locked,
+ /* Do not apply segment base to effective address */
+ X86_SPECIAL_NoSeg,
/*
* Rd/Mb or Rd/Mw in the manual: register operand 0 is treated as 32 bits
* (and writeback zero-extends it to 64 bits if applicable). PREFIX_DATA
@@ -271,16 +278,23 @@ typedef struct X86DecodedOp {
bool has_ea;
int offset; /* For MMX and SSE */
- /*
- * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR,
- * do not access directly!
- */
- TCGv_ptr v_ptr;
+ union {
+ target_ulong imm;
+ /*
+ * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR,
+ * do not access directly!
+ */
+ TCGv_ptr v_ptr;
+ };
} X86DecodedOp;
struct X86DecodedInsn {
X86OpEntry e;
X86DecodedOp op[3];
+ /*
+ * Rightmost immediate, for convenience since most instructions have
+ * one (and also for 4-operand instructions).
+ */
target_ulong immediate;
AddressParts mem;
diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc
index 6bcf88ecd7..2dee33dd48 100644
--- a/target/i386/tcg/emit.c.inc
+++ b/target/i386/tcg/emit.c.inc
@@ -19,6 +19,21 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+/*
+ * Sometimes, knowing what the backend has can produce better code.
+ * The exact opcode to check depends on 32- vs. 64-bit.
+ */
+#ifdef TARGET_X86_64
+#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64
+#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i64_valid
+#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i64_valid
+#else
+#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32
+#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i32_valid
+#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i32_valid
+#endif
+
+
#define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg])
typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg);
@@ -45,6 +60,9 @@ typedef void (*SSEFunc_0_eppppii)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
TCGv_ptr reg_c, TCGv_ptr reg_d, TCGv_i32 even,
TCGv_i32 odd);
+static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode);
+static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode);
+
static inline TCGv_i32 tcg_constant8u_i32(uint8_t val)
{
return tcg_constant_i32(val);
@@ -259,7 +277,7 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v)
}
break;
case X86_OP_IMM:
- tcg_gen_movi_tl(v, decode->immediate);
+ tcg_gen_movi_tl(v, op->imm);
break;
case X86_OP_MMX:
@@ -283,6 +301,8 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v)
static TCGv_ptr op_ptr(X86DecodedInsn *decode, int opn)
{
X86DecodedOp *op = &decode->op[opn];
+
+ assert(op->unit == X86_OP_MMX || op->unit == X86_OP_SSE);
if (op->v_ptr) {
return op->v_ptr;
}
@@ -304,8 +324,8 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv
case X86_OP_SKIP:
break;
case X86_OP_SEG:
- /* Note that gen_movl_seg_T0 takes care of interrupt shadow and TF. */
- gen_movl_seg_T0(s, op->n);
+ /* Note that gen_movl_seg takes care of interrupt shadow and TF. */
+ gen_movl_seg(s, op->n, s->T0);
break;
case X86_OP_INT:
if (op->has_ea) {
@@ -328,6 +348,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv
default:
g_assert_not_reached();
}
+ op->unit = X86_OP_SKIP;
}
static inline int vector_len(DisasContext *s, X86DecodedInsn *decode)
@@ -352,6 +373,20 @@ static void prepare_update2_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op)
decode->cc_op = op;
}
+static void prepare_update_cc_incdec(X86DecodedInsn *decode, DisasContext *s, CCOp op)
+{
+ gen_compute_eflags_c(s, s->T1);
+ prepare_update2_cc(decode, s, op);
+}
+
+static void prepare_update3_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op, TCGv reg)
+{
+ decode->cc_src2 = reg;
+ decode->cc_src = s->T1;
+ decode->cc_dst = s->T0;
+ decode->cc_op = op;
+}
+
static void gen_store_sse(DisasContext *s, X86DecodedInsn *decode, int src_ofs)
{
MemOp ot = decode->op[0].ot;
@@ -1040,6 +1075,53 @@ static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod
VSIB_AVX(VPGATHERD, vpgatherd)
VSIB_AVX(VPGATHERQ, vpgatherq)
+static void gen_AAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_helper_aaa(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+}
+
+static void gen_AAD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_helper_aad(tcg_env, tcg_constant_i32(decode->immediate));
+ set_cc_op(s, CC_OP_LOGICB);
+}
+
+static void gen_AAM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if (decode->immediate == 0) {
+ gen_exception(s, EXCP00_DIVZ);
+ } else {
+ gen_helper_aam(tcg_env, tcg_constant_i32(decode->immediate));
+ set_cc_op(s, CC_OP_LOGICB);
+ }
+}
+
+static void gen_AAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_helper_aas(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+}
+
+static void gen_ADC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ TCGv c_in = tcg_temp_new();
+
+ gen_compute_eflags_c(s, c_in);
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_add_tl(s->T0, c_in, s->T1);
+ tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_add_tl(s->T0, s->T0, s->T1);
+ tcg_gen_add_tl(s->T0, s->T0, c_in);
+ }
+ prepare_update3_cc(decode, s, CC_OP_ADCB + ot, c_in);
+}
+
/* ADCX/ADOX do not have memory operands and can use set_cc_op. */
static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
{
@@ -1093,11 +1175,37 @@ static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADCX);
}
+static void gen_ADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_add_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update2_cc(decode, s, CC_OP_ADDB + ot);
+}
+
static void gen_ADOX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADOX);
}
+static void gen_AND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_and_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_and_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update1_cc(decode, s, CC_OP_LOGICB + ot);
+}
+
static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1106,6 +1214,27 @@ static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
prepare_update1_cc(decode, s, CC_OP_LOGICB + ot);
}
+static void gen_ARPL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGv zf = tcg_temp_new();
+ TCGv flags = tcg_temp_new();
+
+ gen_mov_eflags(s, flags);
+
+ /* Compute adjusted DST in T1, merging in SRC[RPL]. */
+ tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 0, 2);
+
+ /* Z flag set if DST[RPL] < SRC[RPL] */
+ tcg_gen_setcond_tl(TCG_COND_LTU, zf, s->T0, s->T1);
+ tcg_gen_deposit_tl(flags, flags, zf, ctz32(CC_Z), 1);
+
+ /* Place maximum RPL in DST */
+ tcg_gen_umax_tl(s->T0, s->T0, s->T1);
+
+ decode->cc_src = flags;
+ decode->cc_op = CC_OP_EFLAGS;
+}
+
static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1170,6 +1299,28 @@ static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
set_cc_op(s, CC_OP_BMILGB + ot);
}
+static void gen_BOUND(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGv_i32 op = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(op, s->T0);
+ if (decode->op[1].ot == MO_16) {
+ gen_helper_boundw(tcg_env, s->A0, op);
+ } else {
+ gen_helper_boundl(tcg_env, s->A0, op);
+ }
+}
+
+static void gen_BSWAP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+#ifdef TARGET_X86_64
+ if (s->dflag == MO_64) {
+ tcg_gen_bswap64_i64(s->T0, s->T0);
+ return;
+ }
+#endif
+ tcg_gen_bswap32_tl(s->T0, s->T0, TCG_BSWAP_OZ);
+}
+
static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1190,6 +1341,67 @@ static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
prepare_update2_cc(decode, s, CC_OP_BMILGB + ot);
}
+static void gen_CALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_push_v(s, eip_next_tl(s));
+ gen_JMP(s, env, decode);
+}
+
+static void gen_CALL_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_push_v(s, eip_next_tl(s));
+ gen_JMP_m(s, env, decode);
+}
+
+static void gen_CALLF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_far_call(s);
+}
+
+static void gen_CALLF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+
+ gen_op_ld_v(s, ot, s->T0, s->A0);
+ gen_add_A0_im(s, 1 << ot);
+ gen_op_ld_v(s, MO_16, s->T1, s->A0);
+ gen_far_call(s);
+}
+
+static void gen_CBW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp src_ot = decode->op[0].ot - 1;
+
+ tcg_gen_ext_tl(s->T0, s->T0, src_ot | MO_SIGN);
+}
+
+static void gen_CLC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_compute_eflags(s);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C);
+}
+
+static void gen_CLD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUX86State, df));
+}
+
+static void gen_CLI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_reset_eflags(s, IF_MASK);
+}
+
+static void gen_CMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_compute_eflags(s);
+ tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C);
+}
+
+static void gen_CMOVcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_cmovcc1(s, decode->b & 0xf, s->T0, s->T1);
+}
+
static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGLabel *label_top = gen_new_label();
@@ -1209,7 +1421,7 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
[JCC_Z] = TCG_COND_EQ,
[JCC_BE] = TCG_COND_LEU,
[JCC_S] = TCG_COND_LT, /* test sign bit by comparing against 0 */
- [JCC_P] = TCG_COND_EQ, /* even parity - tests low bit of popcount */
+ [JCC_P] = TCG_COND_TSTEQ, /* even parity - tests low bit of popcount */
[JCC_L] = TCG_COND_LT,
[JCC_LE] = TCG_COND_LE,
};
@@ -1260,8 +1472,7 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
case JCC_P:
tcg_gen_ext8u_tl(s->tmp0, s->T0);
tcg_gen_ctpop_tl(s->tmp0, s->tmp0);
- tcg_gen_andi_tl(s->tmp0, s->tmp0, 1);
- cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0);
+ cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(1);
break;
case JCC_S:
@@ -1294,6 +1505,18 @@ static void gen_CMPccXADD(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
decode->cc_op = CC_OP_SUBB + ot;
}
+static void gen_CMPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+ if (s->prefix & PREFIX_REPNZ) {
+ gen_repz_cmps(s, ot, 1);
+ } else if (s->prefix & PREFIX_REPZ) {
+ gen_repz_cmps(s, ot, 0);
+ } else {
+ gen_cmps(s, ot);
+ }
+}
+
static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[2].ot;
@@ -1332,11 +1555,74 @@ static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
}
}
+static void gen_CWD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ int shift = 8 << decode->op[0].ot;
+
+ tcg_gen_sextract_tl(s->T0, s->T0, shift - 1, 1);
+}
+
+static void gen_DAA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_helper_daa(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+}
+
+static void gen_DAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_helper_das(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+}
+
+static void gen_DEC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ tcg_gen_movi_tl(s->T1, -1);
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_add_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot);
+}
+
+static void gen_DIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+
+ switch(ot) {
+ case MO_8:
+ gen_helper_divb_AL(tcg_env, s->T1);
+ break;
+ case MO_16:
+ gen_helper_divw_AX(tcg_env, s->T1);
+ break;
+ default:
+ case MO_32:
+ gen_helper_divl_EAX(tcg_env, s->T1);
+ break;
+#ifdef TARGET_X86_64
+ case MO_64:
+ gen_helper_divq_EAX(tcg_env, s->T1);
+ break;
+#endif
+ }
+}
+
static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
gen_helper_emms(tcg_env);
}
+static void gen_ENTER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_enter(s, decode->op[1].imm, decode->op[2].imm);
+}
+
static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGv_i32 length = tcg_constant_i32(decode->immediate & 63);
@@ -1350,6 +1636,210 @@ static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod
gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2);
}
+static void gen_HLT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+#ifdef CONFIG_SYSTEM_ONLY
+ gen_update_cc_op(s);
+ gen_update_eip_cur(s);
+ gen_helper_hlt(tcg_env, cur_insn_len_i32(s));
+ s->base.is_jmp = DISAS_NORETURN;
+#endif
+}
+
+static void gen_IDIV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+
+ switch(ot) {
+ case MO_8:
+ gen_helper_idivb_AL(tcg_env, s->T1);
+ break;
+ case MO_16:
+ gen_helper_idivw_AX(tcg_env, s->T1);
+ break;
+ default:
+ case MO_32:
+ gen_helper_idivl_EAX(tcg_env, s->T1);
+ break;
+#ifdef TARGET_X86_64
+ case MO_64:
+ gen_helper_idivq_EAX(tcg_env, s->T1);
+ break;
+#endif
+ }
+}
+
+static void gen_IMUL3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[0].ot;
+ TCGv cc_src_rhs;
+
+ switch (ot) {
+ case MO_16:
+ /* s->T0 already sign-extended */
+ tcg_gen_ext16s_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ /* Compare the full result to the extension of the truncated result. */
+ tcg_gen_ext16s_tl(s->T1, s->T0);
+ cc_src_rhs = s->T0;
+ break;
+
+ case MO_32:
+#ifdef TARGET_X86_64
+ if (TCG_TARGET_REG_BITS == 64) {
+ /*
+ * This produces fewer TCG ops, and better code if flags are needed,
+ * but it requires a 64-bit multiply even if they are not. Use it
+ * only if the target has 64-bits registers.
+ *
+ * s->T0 is already sign-extended.
+ */
+ tcg_gen_ext32s_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ /* Compare the full result to the extension of the truncated result. */
+ tcg_gen_ext32s_tl(s->T1, s->T0);
+ cc_src_rhs = s->T0;
+ } else {
+ /* Variant that only needs a 32-bit widening multiply. */
+ TCGv_i32 hi = tcg_temp_new_i32();
+ TCGv_i32 lo = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(lo, s->T0);
+ tcg_gen_trunc_tl_i32(hi, s->T1);
+ tcg_gen_muls2_i32(lo, hi, lo, hi);
+ tcg_gen_extu_i32_tl(s->T0, lo);
+
+ cc_src_rhs = tcg_temp_new();
+ tcg_gen_extu_i32_tl(cc_src_rhs, hi);
+ /* Compare the high part to the sign bit of the truncated result */
+ tcg_gen_sari_i32(lo, lo, 31);
+ tcg_gen_extu_i32_tl(s->T1, lo);
+ }
+ break;
+
+ case MO_64:
+#endif
+ cc_src_rhs = tcg_temp_new();
+ tcg_gen_muls2_tl(s->T0, cc_src_rhs, s->T0, s->T1);
+ /* Compare the high part to the sign bit of the truncated result */
+ tcg_gen_sari_tl(s->T1, s->T0, TARGET_LONG_BITS - 1);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs);
+ prepare_update2_cc(decode, s, CC_OP_MULB + ot);
+}
+
+static void gen_IMUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ TCGv cc_src_rhs;
+
+ switch (ot) {
+ case MO_8:
+ /* s->T0 already sign-extended */
+ tcg_gen_ext8s_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ /* Compare the full result to the extension of the truncated result. */
+ tcg_gen_ext8s_tl(s->T1, s->T0);
+ cc_src_rhs = s->T0;
+ break;
+
+ case MO_16:
+ /* s->T0 already sign-extended */
+ tcg_gen_ext16s_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ tcg_gen_shri_tl(s->T1, s->T0, 16);
+ gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1);
+ /* Compare the full result to the extension of the truncated result. */
+ tcg_gen_ext16s_tl(s->T1, s->T0);
+ cc_src_rhs = s->T0;
+ break;
+
+ case MO_32:
+#ifdef TARGET_X86_64
+ /* s->T0 already sign-extended */
+ tcg_gen_ext32s_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0);
+ tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32);
+ /* Compare the full result to the extension of the truncated result. */
+ tcg_gen_ext32s_tl(s->T1, s->T0);
+ cc_src_rhs = s->T0;
+ break;
+
+ case MO_64:
+#endif
+ tcg_gen_muls2_tl(s->T0, cpu_regs[R_EDX], s->T0, s->T1);
+ tcg_gen_mov_tl(cpu_regs[R_EAX], s->T0);
+
+ /* Compare the high part to the sign bit of the truncated result */
+ tcg_gen_negsetcondi_tl(TCG_COND_LT, s->T1, s->T0, 0);
+ cc_src_rhs = cpu_regs[R_EDX];
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs);
+ prepare_update2_cc(decode, s, CC_OP_MULB + ot);
+}
+
+static void gen_IN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[0].ot;
+ TCGv_i32 port = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(port, s->T1);
+ tcg_gen_ext16u_i32(port, port);
+ if (!gen_check_io(s, ot, port, SVM_IOIO_TYPE_MASK)) {
+ return;
+ }
+ translator_io_start(&s->base);
+ gen_helper_in_func(ot, s->T0, port);
+ gen_writeback(s, decode, 0, s->T0);
+ gen_bpt_io(s, port, ot);
+}
+
+static void gen_INC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ tcg_gen_movi_tl(s->T1, 1);
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_add_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update_cc_incdec(decode, s, CC_OP_INCB + ot);
+}
+
+static void gen_INS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ TCGv_i32 port = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(port, s->T1);
+ tcg_gen_ext16u_i32(port, port);
+ if (!gen_check_io(s, ot, port,
+ SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) {
+ return;
+ }
+
+ translator_io_start(&s->base);
+ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_ins(s, ot);
+ } else {
+ gen_ins(s, ot);
+ }
+}
+
static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGv_i32 length = tcg_constant_i32(decode->immediate & 63);
@@ -1363,12 +1853,197 @@ static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec
gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2);
}
+static void gen_INT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_interrupt(s, decode->immediate);
+}
+
+static void gen_INT1(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_exception(s, EXCP01_DB);
+}
+
+static void gen_INT3(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_interrupt(s, EXCP03_INT3);
+}
+
+static void gen_INTO(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_update_eip_cur(s);
+ gen_helper_into(tcg_env, cur_insn_len_i32(s));
+}
+
+static void gen_IRET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if (!PE(s) || VM86(s)) {
+ gen_helper_iret_real(tcg_env, tcg_constant_i32(s->dflag - 1));
+ } else {
+ gen_helper_iret_protected(tcg_env, tcg_constant_i32(s->dflag - 1),
+ eip_next_i32(s));
+ }
+ set_cc_op(s, CC_OP_EFLAGS);
+ s->base.is_jmp = DISAS_EOB_ONLY;
+}
+
+static void gen_Jcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_bnd_jmp(s);
+ gen_jcc(s, decode->b & 0xf, decode->immediate);
+}
+
+static void gen_JCXZ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGLabel *taken = gen_new_label();
+
+ gen_update_cc_op(s);
+ gen_op_jz_ecx(s, taken);
+ gen_conditional_jump_labels(s, decode->immediate, NULL, taken);
+}
+
+static void gen_JMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_jmp_rel(s, s->dflag, decode->immediate, 0);
+}
+
+static void gen_JMP_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_op_jmp_v(s, s->T0);
+ gen_bnd_jmp(s);
+ s->base.is_jmp = DISAS_JUMP;
+}
+
+static void gen_JMPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_far_jmp(s);
+}
+
+static void gen_JMPF_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+
+ gen_op_ld_v(s, ot, s->T0, s->A0);
+ gen_add_A0_im(s, 1 << ot);
+ gen_op_ld_v(s, MO_16, s->T1, s->A0);
+ gen_far_jmp(s);
+}
+
+static void gen_LAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) {
+ return gen_illegal_opcode(s);
+ }
+ gen_compute_eflags(s);
+ /* Note: gen_compute_eflags() only gives the condition codes */
+ tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02);
+ tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8);
+}
+
static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1);
gen_helper_ldmxcsr(tcg_env, s->tmp2_i32);
}
+static void gen_lxx_seg(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, int seg)
+{
+ MemOp ot = decode->op[0].ot;
+
+ /* Offset already in s->T0. */
+ gen_add_A0_im(s, 1 << ot);
+ gen_op_ld_v(s, MO_16, s->T1, s->A0);
+
+ /* load the segment here to handle exceptions properly */
+ gen_movl_seg(s, seg, s->T1);
+}
+
+static void gen_LDS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_lxx_seg(s, env, decode, R_DS);
+}
+
+static void gen_LEA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ tcg_gen_mov_tl(s->T0, s->A0);
+}
+
+static void gen_LEAVE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_leave(s);
+}
+
+static void gen_LES(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_lxx_seg(s, env, decode, R_ES);
+}
+
+static void gen_LFS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_lxx_seg(s, env, decode, R_FS);
+}
+
+static void gen_LGS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_lxx_seg(s, env, decode, R_GS);
+}
+
+static void gen_LODS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_lods(s, ot);
+ } else {
+ gen_lods(s, ot);
+ }
+}
+
+static void gen_LOOP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGLabel *taken = gen_new_label();
+
+ gen_update_cc_op(s);
+ gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
+ gen_op_jnz_ecx(s, taken);
+ gen_conditional_jump_labels(s, decode->immediate, NULL, taken);
+}
+
+static void gen_LOOPE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGLabel *taken = gen_new_label();
+ TCGLabel *not_taken = gen_new_label();
+
+ gen_update_cc_op(s);
+ gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
+ gen_op_jz_ecx(s, not_taken);
+ gen_jcc1(s, (JCC_Z << 1), taken); /* jz taken */
+ gen_conditional_jump_labels(s, decode->immediate, not_taken, taken);
+}
+
+static void gen_LOOPNE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ TCGLabel *taken = gen_new_label();
+ TCGLabel *not_taken = gen_new_label();
+
+ gen_update_cc_op(s);
+ gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
+ gen_op_jz_ecx(s, not_taken);
+ gen_jcc1(s, (JCC_Z << 1) | 1, taken); /* jnz taken */
+ gen_conditional_jump_labels(s, decode->immediate, not_taken, taken);
+}
+
+static void gen_LSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_lxx_seg(s, env, decode, R_SS);
+}
+
+static void gen_MOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ /* nothing to do! */
+}
+#define gen_NOP gen_MOV
+
static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_DS, s->override);
@@ -1476,6 +2151,67 @@ static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod
return gen_MOVQ(s, env, decode);
}
+static void gen_MOVS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_movs(s, ot);
+ } else {
+ gen_movs(s, ot);
+ }
+}
+
+static void gen_MUL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ switch (ot) {
+ case MO_8:
+ /* s->T0 already zero-extended */
+ tcg_gen_ext8u_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ tcg_gen_andi_tl(s->T1, s->T0, 0xff00);
+ decode->cc_dst = s->T0;
+ decode->cc_src = s->T1;
+ break;
+
+ case MO_16:
+ /* s->T0 already zero-extended */
+ tcg_gen_ext16u_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ tcg_gen_shri_tl(s->T1, s->T0, 16);
+ gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1);
+ decode->cc_dst = s->T0;
+ decode->cc_src = s->T1;
+ break;
+
+ case MO_32:
+#ifdef TARGET_X86_64
+ /* s->T0 already zero-extended */
+ tcg_gen_ext32u_tl(s->T1, s->T1);
+ tcg_gen_mul_tl(s->T0, s->T0, s->T1);
+ tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0);
+ tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32);
+ decode->cc_dst = cpu_regs[R_EAX];
+ decode->cc_src = cpu_regs[R_EDX];
+ break;
+
+ case MO_64:
+#endif
+ tcg_gen_mulu2_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->T0, s->T1);
+ decode->cc_dst = cpu_regs[R_EAX];
+ decode->cc_src = cpu_regs[R_EDX];
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ decode->cc_op = CC_OP_MULB + ot;
+}
+
static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1502,6 +2238,95 @@ static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
}
}
+static void gen_NEG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[0].ot;
+ TCGv oldv = tcg_temp_new();
+
+ if (s->prefix & PREFIX_LOCK) {
+ TCGv newv = tcg_temp_new();
+ TCGv cmpv = tcg_temp_new();
+ TCGLabel *label1 = gen_new_label();
+
+ gen_set_label(label1);
+ gen_op_ld_v(s, ot, oldv, s->A0);
+ tcg_gen_neg_tl(newv, oldv);
+ tcg_gen_atomic_cmpxchg_tl(cmpv, s->A0, oldv, newv,
+ s->mem_index, ot | MO_LE);
+ tcg_gen_brcond_tl(TCG_COND_NE, oldv, cmpv, label1);
+ } else {
+ tcg_gen_mov_tl(oldv, s->T0);
+ }
+ tcg_gen_neg_tl(s->T0, oldv);
+
+ decode->cc_dst = s->T0;
+ decode->cc_src = oldv;
+ tcg_gen_movi_tl(s->cc_srcT, 0);
+ decode->cc_op = CC_OP_SUBB + ot;
+}
+
+static void gen_NOT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[0].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_movi_tl(s->T0, ~0);
+ tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_not_tl(s->T0, s->T0);
+ }
+}
+
+static void gen_OR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_or_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_or_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update1_cc(decode, s, CC_OP_LOGICB + ot);
+}
+
+static void gen_OUT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ TCGv_i32 port = tcg_temp_new_i32();
+ TCGv_i32 value = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(port, s->T1);
+ tcg_gen_ext16u_i32(port, port);
+ if (!gen_check_io(s, ot, port, 0)) {
+ return;
+ }
+ tcg_gen_trunc_tl_i32(value, s->T0);
+ translator_io_start(&s->base);
+ gen_helper_out_func(ot, port, value);
+ gen_bpt_io(s, port, ot);
+}
+
+static void gen_OUTS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ TCGv_i32 port = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(port, s->T1);
+ tcg_gen_ext16u_i32(port, port);
+ if (!gen_check_io(s, ot, port, SVM_IOIO_STR_MASK)) {
+ return;
+ }
+
+ translator_io_start(&s->base);
+ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_outs(s, ot);
+ } else {
+ gen_outs(s, ot);
+ }
+}
+
static void gen_PALIGNR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGv_i32 imm = tcg_constant8u_i32(decode->immediate);
@@ -1695,12 +2520,6 @@ static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s)
tcg_gen_or_vec(vece, d, d, t);
}
-#ifdef TARGET_X86_64
-#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64
-#else
-#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32
-#endif
-
static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 };
@@ -1745,6 +2564,45 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco
}
}
+static void gen_POP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = gen_pop_T0(s);
+ if (decode->op[0].has_ea) {
+ /* NOTE: order is important for MMU exceptions */
+ gen_op_st_v(s, ot, s->T0, s->A0);
+ decode->op[0].unit = X86_OP_SKIP;
+ }
+ /* NOTE: writing back registers after update is important for pop %sp */
+ gen_pop_update(s, ot);
+}
+
+static void gen_POPA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_popa(s);
+}
+
+static void gen_POPF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot;
+ int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK;
+
+ if (CPL(s) == 0) {
+ mask |= IF_MASK | IOPL_MASK;
+ } else if (CPL(s) <= IOPL(s)) {
+ mask |= IF_MASK;
+ }
+ if (s->dflag == MO_16) {
+ mask &= 0xffff;
+ }
+
+ ot = gen_pop_T0(s);
+ gen_helper_write_eflags(tcg_env, s->T0, tcg_constant_i32(mask));
+ gen_pop_update(s, ot);
+ set_cc_op(s, CC_OP_EFLAGS);
+ /* abort translation because TF/AC flag may change */
+ s->base.is_jmp = DISAS_EOB_NEXT;
+}
+
static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGv_i32 imm = tcg_constant8u_i32(decode->immediate);
@@ -1891,6 +2749,455 @@ static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco
}
}
+static void gen_PUSH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_push_v(s, s->T1);
+}
+
+static void gen_PUSHA(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_pusha(s);
+}
+
+static void gen_PUSHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_update_cc_op(s);
+ gen_helper_read_eflags(s->T0, tcg_env);
+ gen_push_v(s, s->T0);
+}
+
+static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode,
+ bool *can_be_zero, TCGv *count)
+{
+ MemOp ot = decode->op[0].ot;
+ int mask = (ot <= MO_32 ? 0x1f : 0x3f);
+
+ *can_be_zero = false;
+ switch (decode->op[2].unit) {
+ case X86_OP_INT:
+ *count = tcg_temp_new();
+ tcg_gen_andi_tl(*count, s->T1, mask);
+ *can_be_zero = true;
+ break;
+
+ case X86_OP_IMM:
+ if ((decode->immediate & mask) == 0) {
+ *count = NULL;
+ break;
+ }
+ *count = tcg_temp_new();
+ tcg_gen_movi_tl(*count, decode->immediate & mask);
+ break;
+
+ case X86_OP_SKIP:
+ *count = tcg_temp_new();
+ tcg_gen_movi_tl(*count, 1);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ return ot;
+}
+
+/*
+ * Compute existing flags in decode->cc_src, for gen_* functions that wants
+ * to set the cc_op set to CC_OP_ADCOX. In particular, this allows rotate
+ * operations to compute the carry in decode->cc_dst and the overflow in
+ * decode->cc_src2.
+ *
+ * If need_flags is true, decode->cc_dst and decode->cc_src2 are preloaded
+ * with the value of CF and OF before the instruction, so that it is possible
+ * to keep the flags unmodified.
+ *
+ * Return true if carry could be made available cheaply as a 1-bit value in
+ * decode->cc_dst (trying a bit harder if want_carry is true). If false is
+ * returned, decode->cc_dst is uninitialized and the carry is only available
+ * as bit 0 of decode->cc_src.
+ */
+static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_carry, bool need_flags)
+{
+ bool got_cf = false;
+ bool got_of = false;
+
+ decode->cc_dst = tcg_temp_new();
+ decode->cc_src = tcg_temp_new();
+ decode->cc_src2 = tcg_temp_new();
+ decode->cc_op = CC_OP_ADCOX;
+
+ /* A lot more cc_ops could be "optimized" to avoid the extracts at
+ * the end (INC/DEC, BMILG, MUL), but they are all really unlikely
+ * to be followed by rotations within the same basic block.
+ */
+ switch (s->cc_op) {
+ case CC_OP_ADCOX:
+ /* No need to compute the full EFLAGS, CF/OF are already isolated. */
+ tcg_gen_mov_tl(decode->cc_src, cpu_cc_src);
+ if (need_flags) {
+ tcg_gen_mov_tl(decode->cc_src2, cpu_cc_src2);
+ got_of = true;
+ }
+ if (want_carry || need_flags) {
+ tcg_gen_mov_tl(decode->cc_dst, cpu_cc_dst);
+ got_cf = true;
+ }
+ break;
+
+ case CC_OP_LOGICB ... CC_OP_LOGICQ:
+ /* CF and OF are zero, do it just because it's easy. */
+ gen_mov_eflags(s, decode->cc_src);
+ if (need_flags) {
+ tcg_gen_movi_tl(decode->cc_src2, 0);
+ got_of = true;
+ }
+ if (want_carry || need_flags) {
+ tcg_gen_movi_tl(decode->cc_dst, 0);
+ got_cf = true;
+ }
+ break;
+
+ case CC_OP_SARB ... CC_OP_SARQ:
+ /*
+ * SHR/RCR/SHR/RCR/... is a relatively common occurrence of RCR.
+ * By computing CF without using eflags, the calls to cc_compute_all
+ * can be eliminated as dead code (except for the last RCR).
+ */
+ if (want_carry || need_flags) {
+ tcg_gen_andi_tl(decode->cc_dst, cpu_cc_src, 1);
+ got_cf = true;
+ }
+ gen_mov_eflags(s, decode->cc_src);
+ break;
+
+ case CC_OP_SHLB ... CC_OP_SHLQ:
+ /*
+ * Likewise for SHL/RCL/SHL/RCL/... but, if CF is not in the sign
+ * bit, we might as well fish CF out of EFLAGS and save a shift.
+ */
+ if (want_carry && (!need_flags || s->cc_op == CC_OP_SHLB + MO_TL)) {
+ tcg_gen_shri_tl(decode->cc_dst, cpu_cc_src, (8 << (s->cc_op - CC_OP_SHLB)) - 1);
+ got_cf = true;
+ }
+ gen_mov_eflags(s, decode->cc_src);
+ break;
+
+ default:
+ gen_mov_eflags(s, decode->cc_src);
+ break;
+ }
+
+ if (need_flags) {
+ /* If the flags could be left unmodified, always load them. */
+ if (!got_of) {
+ tcg_gen_extract_tl(decode->cc_src2, decode->cc_src, ctz32(CC_O), 1);
+ got_of = true;
+ }
+ if (!got_cf) {
+ tcg_gen_extract_tl(decode->cc_dst, decode->cc_src, ctz32(CC_C), 1);
+ got_cf = true;
+ }
+ }
+ return got_cf;
+}
+
+static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, TCGv count)
+{
+ MemOp ot = decode->op[0].ot;
+ TCGv temp = count ? tcg_temp_new() : decode->cc_src2;
+
+ tcg_gen_xor_tl(temp, old, result);
+ tcg_gen_extract_tl(temp, temp, (8 << ot) - 1, 1);
+ if (count) {
+ tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src2, count, tcg_constant_tl(0),
+ decode->cc_src2, temp);
+ }
+}
+
+/*
+ * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands,
+ * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further.
+ */
+static void gen_rotc_mod(MemOp ot, TCGv count)
+{
+ TCGv temp;
+
+ switch (ot) {
+ case MO_8:
+ temp = tcg_temp_new();
+ tcg_gen_subi_tl(temp, count, 18);
+ tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count);
+ tcg_gen_subi_tl(temp, count, 9);
+ tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count);
+ break;
+
+ case MO_16:
+ temp = tcg_temp_new();
+ tcg_gen_subi_tl(temp, count, 17);
+ tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * The idea here is that the bit to the right of the new bit 0 is the
+ * new carry, and the bit to the right of the old bit 0 is the old carry.
+ * Just like a regular rotation, the result of the rotation is composed
+ * from a right shifted part and a left shifted part of s->T0. The new carry
+ * is extracted from the right-shifted portion, and the old carry is
+ * inserted at the end of the left-shifted portion.
+ *
+ * Because of the separate shifts involving the carry, gen_RCL and gen_RCR
+ * mostly operate on count-1. This also comes in handy when computing
+ * length - count, because (length-1) - (count-1) can be computed with
+ * a XOR, and that is commutative unlike subtraction.
+ */
+static void gen_RCL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool have_1bit_cin, can_be_zero;
+ TCGv count;
+ TCGLabel *zero_label = NULL;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+ TCGv low, high, low_count;
+
+ if (!count) {
+ return;
+ }
+
+ low = tcg_temp_new();
+ high = tcg_temp_new();
+ low_count = tcg_temp_new();
+
+ gen_rotc_mod(ot, count);
+ have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero);
+ if (can_be_zero) {
+ zero_label = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label);
+ }
+
+ /* Compute high part, including incoming carry. */
+ if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) {
+ /* high = (T0 << 1) | cin */
+ TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src;
+ tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1);
+ } else {
+ /* Same as above but without deposit; cin in cc_dst. */
+ tcg_gen_add_tl(high, s->T0, decode->cc_dst);
+ tcg_gen_add_tl(high, high, s->T0);
+ }
+ tcg_gen_subi_tl(count, count, 1);
+ tcg_gen_shl_tl(high, high, count);
+
+ /* Compute low part and outgoing carry, incoming s->T0 is zero extended */
+ tcg_gen_xori_tl(low_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */
+ tcg_gen_shr_tl(low, s->T0, low_count);
+ tcg_gen_andi_tl(decode->cc_dst, low, 1);
+ tcg_gen_shri_tl(low, low, 1);
+
+ /* Compute result and outgoing overflow */
+ tcg_gen_mov_tl(decode->cc_src2, s->T0);
+ tcg_gen_or_tl(s->T0, low, high);
+ gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL);
+
+ if (zero_label) {
+ gen_set_label(zero_label);
+ }
+}
+
+static void gen_RCR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool have_1bit_cin, can_be_zero;
+ TCGv count;
+ TCGLabel *zero_label = NULL;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+ TCGv low, high, high_count;
+
+ if (!count) {
+ return;
+ }
+
+ low = tcg_temp_new();
+ high = tcg_temp_new();
+ high_count = tcg_temp_new();
+
+ gen_rotc_mod(ot, count);
+ have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero);
+ if (can_be_zero) {
+ zero_label = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label);
+ }
+
+ /* Save incoming carry into high, it will be shifted later. */
+ if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) {
+ TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src;
+ tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1);
+ } else {
+ /* Same as above but without deposit; cin in cc_dst. */
+ tcg_gen_add_tl(high, s->T0, decode->cc_dst);
+ tcg_gen_add_tl(high, high, s->T0);
+ }
+
+ /* Compute low part and outgoing carry, incoming s->T0 is zero extended */
+ tcg_gen_subi_tl(count, count, 1);
+ tcg_gen_shr_tl(low, s->T0, count);
+ tcg_gen_andi_tl(decode->cc_dst, low, 1);
+ tcg_gen_shri_tl(low, low, 1);
+
+ /* Move high part to the right position */
+ tcg_gen_xori_tl(high_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */
+ tcg_gen_shl_tl(high, high, high_count);
+
+ /* Compute result and outgoing overflow */
+ tcg_gen_mov_tl(decode->cc_src2, s->T0);
+ tcg_gen_or_tl(s->T0, low, high);
+ gen_rot_overflow(decode, s->T0, decode->cc_src2, NULL);
+
+ if (zero_label) {
+ gen_set_label(zero_label);
+ }
+}
+
+static void gen_RET(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0;
+
+ MemOp ot = gen_pop_T0(s);
+ gen_stack_update(s, adjust + (1 << ot));
+ gen_op_jmp_v(s, s->T0);
+ gen_bnd_jmp(s);
+ s->base.is_jmp = DISAS_JUMP;
+}
+
+static void gen_RETF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ int16_t adjust = decode->e.op2 == X86_TYPE_I ? decode->immediate : 0;
+
+ if (!PE(s) || VM86(s)) {
+ gen_stack_A0(s);
+ /* pop offset */
+ gen_op_ld_v(s, s->dflag, s->T0, s->A0);
+ /* NOTE: keeping EIP updated is not a problem in case of
+ exception */
+ gen_op_jmp_v(s, s->T0);
+ /* pop selector */
+ gen_add_A0_im(s, 1 << s->dflag);
+ gen_op_ld_v(s, s->dflag, s->T0, s->A0);
+ gen_op_movl_seg_real(s, R_CS, s->T0);
+ /* add stack offset */
+ gen_stack_update(s, adjust + (2 << s->dflag));
+ } else {
+ gen_update_cc_op(s);
+ gen_update_eip_cur(s);
+ gen_helper_lret_protected(tcg_env, tcg_constant_i32(s->dflag - 1),
+ tcg_constant_i32(adjust));
+ }
+ s->base.is_jmp = DISAS_EOB_ONLY;
+}
+
+/*
+ * Return non-NULL if a 32-bit rotate works, after possibly replicating the input.
+ * The input has already been zero-extended upon operand decode.
+ */
+static TCGv_i32 gen_rot_replicate(MemOp ot, TCGv in)
+{
+ TCGv_i32 temp;
+ switch (ot) {
+ case MO_8:
+ temp = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(temp, in);
+ tcg_gen_muli_i32(temp, temp, 0x01010101);
+ return temp;
+
+ case MO_16:
+ temp = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(temp, in);
+ tcg_gen_deposit_i32(temp, temp, temp, 16, 16);
+ return temp;
+
+#ifdef TARGET_X86_64
+ case MO_32:
+ temp = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(temp, in);
+ return temp;
+#endif
+
+ default:
+ return NULL;
+ }
+}
+
+static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, TCGv count, int bit)
+{
+ if (count == NULL) {
+ tcg_gen_extract_tl(decode->cc_dst, result, bit, 1);
+ } else {
+ TCGv temp = tcg_temp_new();
+ tcg_gen_extract_tl(temp, result, bit, 1);
+ tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0),
+ decode->cc_dst, temp);
+ }
+}
+
+static void gen_ROL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool can_be_zero;
+ TCGv count;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+ TCGv_i32 temp32, count32;
+ TCGv old = tcg_temp_new();
+
+ if (!count) {
+ return;
+ }
+
+ gen_eflags_adcox(s, decode, false, can_be_zero);
+ tcg_gen_mov_tl(old, s->T0);
+ temp32 = gen_rot_replicate(ot, s->T0);
+ if (temp32) {
+ count32 = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(count32, count);
+ tcg_gen_rotl_i32(temp32, temp32, count32);
+ /* Zero extend to facilitate later optimization. */
+ tcg_gen_extu_i32_tl(s->T0, temp32);
+ } else {
+ tcg_gen_rotl_tl(s->T0, s->T0, count);
+ }
+ gen_rot_carry(decode, s->T0, count, 0);
+ gen_rot_overflow(decode, s->T0, old, count);
+}
+
+static void gen_ROR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool can_be_zero;
+ TCGv count;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+ TCGv_i32 temp32, count32;
+ TCGv old = tcg_temp_new();
+
+ if (!count) {
+ return;
+ }
+
+ gen_eflags_adcox(s, decode, false, can_be_zero);
+ tcg_gen_mov_tl(old, s->T0);
+ temp32 = gen_rot_replicate(ot, s->T0);
+ if (temp32) {
+ count32 = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(count32, count);
+ tcg_gen_rotr_i32(temp32, temp32, count32);
+ /* Zero extend to facilitate later optimization. */
+ tcg_gen_extu_i32_tl(s->T0, temp32);
+ gen_rot_carry(decode, s->T0, count, 31);
+ } else {
+ tcg_gen_rotr_tl(s->T0, s->T0, count);
+ gen_rot_carry(decode, s->T0, count, TARGET_LONG_BITS - 1);
+ }
+ gen_rot_overflow(decode, s->T0, old, count);
+}
+
static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1915,6 +3222,76 @@ static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
}
}
+static void gen_SAHF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) {
+ return gen_illegal_opcode(s);
+ }
+ tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8);
+ gen_compute_eflags(s);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O);
+ tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0);
+}
+
+static void gen_SALC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_compute_eflags_c(s, s->T0);
+ tcg_gen_neg_tl(s->T0, s->T0);
+}
+
+static void gen_shift_dynamic_flags(DisasContext *s, X86DecodedInsn *decode, TCGv count, CCOp cc_op)
+{
+ TCGv_i32 count32 = tcg_temp_new_i32();
+ TCGv_i32 old_cc_op;
+
+ decode->cc_op = CC_OP_DYNAMIC;
+ decode->cc_op_dynamic = tcg_temp_new_i32();
+
+ assert(decode->cc_dst == s->T0);
+ if (cc_op_live[s->cc_op] & USES_CC_DST) {
+ decode->cc_dst = tcg_temp_new();
+ tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0),
+ cpu_cc_dst, s->T0);
+ }
+
+ if (cc_op_live[s->cc_op] & USES_CC_SRC) {
+ tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src, count, tcg_constant_tl(0),
+ cpu_cc_src, decode->cc_src);
+ }
+
+ tcg_gen_trunc_tl_i32(count32, count);
+ if (s->cc_op == CC_OP_DYNAMIC) {
+ old_cc_op = cpu_cc_op;
+ } else {
+ old_cc_op = tcg_constant_i32(s->cc_op);
+ }
+ tcg_gen_movcond_i32(TCG_COND_EQ, decode->cc_op_dynamic, count32, tcg_constant_i32(0),
+ old_cc_op, tcg_constant_i32(cc_op));
+}
+
+static void gen_SAR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool can_be_zero;
+ TCGv count;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+
+ if (!count) {
+ return;
+ }
+
+ decode->cc_dst = s->T0;
+ decode->cc_src = tcg_temp_new();
+ tcg_gen_subi_tl(decode->cc_src, count, 1);
+ tcg_gen_sar_tl(decode->cc_src, s->T0, decode->cc_src);
+ tcg_gen_sar_tl(s->T0, s->T0, count);
+ if (can_be_zero) {
+ gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot);
+ } else {
+ decode->cc_op = CC_OP_SARB + ot;
+ }
+}
+
static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1925,6 +3302,45 @@ static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
tcg_gen_sar_tl(s->T0, s->T0, s->T1);
}
+static void gen_SBB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[0].ot;
+ TCGv c_in = tcg_temp_new();
+
+ gen_compute_eflags_c(s, c_in);
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_add_tl(s->T0, s->T1, c_in);
+ tcg_gen_neg_tl(s->T0, s->T0);
+ tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0,
+ s->mem_index, ot | MO_LE);
+ } else {
+ /*
+ * TODO: SBB reg, reg could use gen_prepare_eflags_c followed by
+ * negsetcond, and CC_OP_SUBB as the cc_op.
+ */
+ tcg_gen_sub_tl(s->T0, s->T0, s->T1);
+ tcg_gen_sub_tl(s->T0, s->T0, c_in);
+ }
+ prepare_update3_cc(decode, s, CC_OP_SBBB + ot, c_in);
+}
+
+static void gen_SCAS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[2].ot;
+ if (s->prefix & PREFIX_REPNZ) {
+ gen_repz_scas(s, ot, 1);
+ } else if (s->prefix & PREFIX_REPZ) {
+ gen_repz_scas(s, ot, 0);
+ } else {
+ gen_scas(s, ot);
+ }
+}
+
+static void gen_SETcc(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_setcc1(s, decode->b & 0xf, s->T0);
+}
+
static void gen_SHA1NEXTE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2);
@@ -1979,6 +3395,28 @@ static void gen_SHA256RNDS2(DisasContext *s, CPUX86State *env, X86DecodedInsn *d
gen_helper_sha256rnds2(OP_PTR0, OP_PTR1, OP_PTR2, wk0, wk1);
}
+static void gen_SHL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool can_be_zero;
+ TCGv count;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+
+ if (!count) {
+ return;
+ }
+
+ decode->cc_dst = s->T0;
+ decode->cc_src = tcg_temp_new();
+ tcg_gen_subi_tl(decode->cc_src, count, 1);
+ tcg_gen_shl_tl(decode->cc_src, s->T0, decode->cc_src);
+ tcg_gen_shl_tl(s->T0, s->T0, count);
+ if (can_be_zero) {
+ gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot);
+ } else {
+ decode->cc_op = CC_OP_SHLB + ot;
+ }
+}
+
static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1989,6 +3427,28 @@ static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
tcg_gen_shl_tl(s->T0, s->T0, s->T1);
}
+static void gen_SHR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ bool can_be_zero;
+ TCGv count;
+ MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count);
+
+ if (!count) {
+ return;
+ }
+
+ decode->cc_dst = s->T0;
+ decode->cc_src = tcg_temp_new();
+ tcg_gen_subi_tl(decode->cc_src, count, 1);
+ tcg_gen_shr_tl(decode->cc_src, s->T0, decode->cc_src);
+ tcg_gen_shr_tl(s->T0, s->T0, count);
+ if (can_be_zero) {
+ gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot);
+ } else {
+ decode->cc_op = CC_OP_SARB + ot;
+ }
+}
+
static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
MemOp ot = decode->op[0].ot;
@@ -1999,6 +3459,25 @@ static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
tcg_gen_shr_tl(s->T0, s->T0, s->T1);
}
+static void gen_STC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_compute_eflags(s);
+ tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C);
+}
+
+static void gen_STD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ tcg_gen_st_i32(tcg_constant_i32(-1), tcg_env, offsetof(CPUX86State, df));
+}
+
+static void gen_STI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_set_eflags(s, IF_MASK);
+ /* interruptions are enabled only the first insn after sti */
+ gen_update_eip_next(s);
+ gen_eob_inhibit_irq(s);
+}
+
static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
TCGv_i32 imm = tcg_constant8u_i32(decode->immediate);
@@ -2012,6 +3491,37 @@ static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod
tcg_gen_ld32u_tl(s->T0, tcg_env, offsetof(CPUX86State, mxcsr));
}
+static void gen_STOS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+ if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_stos(s, ot);
+ } else {
+ gen_stos(s, ot);
+ }
+}
+
+static void gen_SUB(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ MemOp ot = decode->op[1].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_neg_tl(s->T0, s->T1);
+ tcg_gen_atomic_fetch_add_tl(s->cc_srcT, s->A0, s->T0,
+ s->mem_index, ot | MO_LE);
+ tcg_gen_sub_tl(s->T0, s->cc_srcT, s->T1);
+ } else {
+ tcg_gen_mov_tl(s->cc_srcT, s->T0);
+ tcg_gen_sub_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update2_cc(decode, s, CC_OP_SUBB + ot);
+}
+
+static void gen_UD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ gen_illegal_opcode(s);
+}
+
static void gen_VAESIMC(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
{
assert(!s->vex_l);
@@ -2491,3 +4001,69 @@ static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *de
tcg_gen_gvec_dup_imm(MO_64, offset, 16, 16, 0);
}
}
+
+static void gen_WAIT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) {
+ gen_NM_exception(s);
+ } else {
+ /* needs to be treated as I/O because of ferr_irq */
+ translator_io_start(&s->base);
+ gen_helper_fwait(tcg_env);
+ }
+}
+
+static void gen_XCHG(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ if (decode->b == 0x90 && !REX_B(s)) {
+ if (s->prefix & PREFIX_REPZ) {
+ gen_update_cc_op(s);
+ gen_update_eip_cur(s);
+ gen_helper_pause(tcg_env, cur_insn_len_i32(s));
+ s->base.is_jmp = DISAS_NORETURN;
+ }
+ /* No writeback. */
+ decode->op[0].unit = X86_OP_SKIP;
+ return;
+ }
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_xchg_tl(s->T0, s->A0, s->T1,
+ s->mem_index, decode->op[0].ot | MO_LE);
+ /* now store old value into register operand */
+ gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0);
+ } else {
+ /* move destination value into source operand, source preserved in T1 */
+ gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0);
+ tcg_gen_mov_tl(s->T0, s->T1);
+ }
+}
+
+static void gen_XLAT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ /* AL is already zero-extended into s->T0. */
+ tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0);
+ gen_add_A0_ds_seg(s);
+ gen_op_ld_v(s, MO_8, s->T0, s->A0);
+}
+
+static void gen_XOR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
+{
+ /* special case XOR reg, reg */
+ if (decode->op[1].unit == X86_OP_INT &&
+ decode->op[2].unit == X86_OP_INT &&
+ decode->op[1].n == decode->op[2].n) {
+ tcg_gen_movi_tl(s->T0, 0);
+ decode->cc_op = CC_OP_CLR;
+ } else {
+ MemOp ot = decode->op[1].ot;
+
+ if (s->prefix & PREFIX_LOCK) {
+ tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T1,
+ s->mem_index, ot | MO_LE);
+ } else {
+ tcg_gen_xor_tl(s->T0, s->T0, s->T1);
+ }
+ prepare_update1_cc(decode, s, CC_OP_LOGICB + ot);
+ }
+}
diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c
index ab85dc5540..df16130f5d 100644
--- a/target/i386/tcg/int_helper.c
+++ b/target/i386/tcg/int_helper.c
@@ -29,22 +29,6 @@
//#define DEBUG_MULDIV
-/* modulo 9 table */
-static const uint8_t rclb_table[32] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 0, 1, 2, 3, 4, 5,
- 6, 7, 8, 0, 1, 2, 3, 4,
-};
-
-/* modulo 17 table */
-static const uint8_t rclw_table[32] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 0, 1, 2, 3, 4, 5, 6,
- 7, 8, 9, 10, 11, 12, 13, 14,
-};
-
/* division, flags are undefined */
void helper_divb_AL(CPUX86State *env, target_ulong t0)
@@ -447,24 +431,6 @@ target_ulong helper_pext(target_ulong src, target_ulong mask)
return dest;
}
-#define SHIFT 0
-#include "shift_helper_template.h.inc"
-#undef SHIFT
-
-#define SHIFT 1
-#include "shift_helper_template.h.inc"
-#undef SHIFT
-
-#define SHIFT 2
-#include "shift_helper_template.h.inc"
-#undef SHIFT
-
-#ifdef TARGET_X86_64
-#define SHIFT 3
-#include "shift_helper_template.h.inc"
-#undef SHIFT
-#endif
-
/* Test that BIT is enabled in CR4. If not, raise an illegal opcode
exception. This reduces the requirements for rare CR4 bits being
mapped into HFLAGS. */
diff --git a/target/i386/tcg/shift_helper_template.h.inc b/target/i386/tcg/shift_helper_template.h.inc
deleted file mode 100644
index 54f15d6e05..0000000000
--- a/target/i386/tcg/shift_helper_template.h.inc
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * x86 shift helpers
- *
- * Copyright (c) 2008 Fabrice Bellard
- *
- * 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.1 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/>.
- */
-
-#define DATA_BITS (1 << (3 + SHIFT))
-#define SHIFT_MASK (DATA_BITS - 1)
-#if DATA_BITS <= 32
-#define SHIFT1_MASK 0x1f
-#else
-#define SHIFT1_MASK 0x3f
-#endif
-
-#if DATA_BITS == 8
-#define SUFFIX b
-#define DATA_MASK 0xff
-#elif DATA_BITS == 16
-#define SUFFIX w
-#define DATA_MASK 0xffff
-#elif DATA_BITS == 32
-#define SUFFIX l
-#define DATA_MASK 0xffffffff
-#elif DATA_BITS == 64
-#define SUFFIX q
-#define DATA_MASK 0xffffffffffffffffULL
-#else
-#error unhandled operand size
-#endif
-
-target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0,
- target_ulong t1)
-{
- int count, eflags;
- target_ulong src;
- target_long res;
-
- count = t1 & SHIFT1_MASK;
-#if DATA_BITS == 16
- count = rclw_table[count];
-#elif DATA_BITS == 8
- count = rclb_table[count];
-#endif
- if (count) {
- eflags = env->cc_src;
- t0 &= DATA_MASK;
- src = t0;
- res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1));
- if (count > 1) {
- res |= t0 >> (DATA_BITS + 1 - count);
- }
- t0 = res;
- env->cc_src = (eflags & ~(CC_C | CC_O)) |
- (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) |
- ((src >> (DATA_BITS - count)) & CC_C);
- }
- return t0;
-}
-
-target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0,
- target_ulong t1)
-{
- int count, eflags;
- target_ulong src;
- target_long res;
-
- count = t1 & SHIFT1_MASK;
-#if DATA_BITS == 16
- count = rclw_table[count];
-#elif DATA_BITS == 8
- count = rclb_table[count];
-#endif
- if (count) {
- eflags = env->cc_src;
- t0 &= DATA_MASK;
- src = t0;
- res = (t0 >> count) |
- ((target_ulong)(eflags & CC_C) << (DATA_BITS - count));
- if (count > 1) {
- res |= t0 << (DATA_BITS + 1 - count);
- }
- t0 = res;
- env->cc_src = (eflags & ~(CC_C | CC_O)) |
- (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) |
- ((src >> (count - 1)) & CC_C);
- }
- return t0;
-}
-
-#undef DATA_BITS
-#undef SHIFT_MASK
-#undef SHIFT1_MASK
-#undef DATA_TYPE
-#undef DATA_MASK
-#undef SUFFIX
diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c
index 7a57b7dd10..8fb05b1f53 100644
--- a/target/i386/tcg/sysemu/excp_helper.c
+++ b/target/i386/tcg/sysemu/excp_helper.c
@@ -21,6 +21,7 @@
#include "cpu.h"
#include "exec/cpu_ldst.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "tcg/helper-tcg.h"
typedef struct TranslateParams {
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index c05d9e5225..76be742580 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -20,11 +20,9 @@
#include "qemu/host-utils.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
-#include "exec/cpu_ldst.h"
#include "exec/translator.h"
#include "fpu/softfloat.h"
@@ -38,6 +36,9 @@
#include "exec/helper-info.c.inc"
#undef HELPER_H
+/* Fixes for Windows namespace pollution. */
+#undef IN
+#undef OUT
#define PREFIX_REPZ 0x01
#define PREFIX_REPNZ 0x02
@@ -212,7 +213,6 @@ typedef struct DisasContext {
#ifdef CONFIG_USER_ONLY
STUB_HELPER(clgi, TCGv_env env)
STUB_HELPER(flush_page, TCGv_env env, TCGv addr)
-STUB_HELPER(hlt, TCGv_env env, TCGv_i32 pc_ofs)
STUB_HELPER(inb, TCGv ret, TCGv_env env, TCGv_i32 port)
STUB_HELPER(inw, TCGv ret, TCGv_env env, TCGv_i32 port)
STUB_HELPER(inl, TCGv ret, TCGv_env env, TCGv_i32 port)
@@ -239,21 +239,8 @@ static void gen_eob(DisasContext *s);
static void gen_jr(DisasContext *s);
static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num);
static void gen_jmp_rel_csize(DisasContext *s, int diff, int tb_num);
-static void gen_op(DisasContext *s1, int op, MemOp ot, int d);
static void gen_exception_gpf(DisasContext *s);
-/* i386 arith/logic operations */
-enum {
- OP_ADDL,
- OP_ORL,
- OP_ADCL,
- OP_SBBL,
- OP_ANDL,
- OP_SUBL,
- OP_XORL,
- OP_CMPL,
-};
-
/* i386 shift ops */
enum {
OP_ROL,
@@ -422,16 +409,6 @@ static inline MemOp mo_stacksize(DisasContext *s)
return CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16;
}
-/* Select only size 64 else 32. Used for SSE operand sizes. */
-static inline MemOp mo_64_32(MemOp ot)
-{
-#ifdef TARGET_X86_64
- return ot == MO_64 ? MO_64 : MO_32;
-#else
- return MO_32;
-#endif
-}
-
/* Select size 8 if lsb of B is clear, else OT. Used for decoding
byte vs word opcodes. */
static inline MemOp mo_b_d(int b, MemOp ot)
@@ -439,13 +416,6 @@ static inline MemOp mo_b_d(int b, MemOp ot)
return b & 1 ? ot : MO_8;
}
-/* Select size 8 if lsb of B is clear, else OT capped at 32.
- Used for decoding operand size of port opcodes. */
-static inline MemOp mo_b_d32(int b, MemOp ot)
-{
- return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8;
-}
-
/* Compute the result of writing t0 to the OT-sized register REG.
*
* If DEST is NULL, store the result into the register and return the
@@ -848,25 +818,6 @@ static void gen_op_update2_cc(DisasContext *s)
tcg_gen_mov_tl(cpu_cc_dst, s->T0);
}
-static void gen_op_update3_cc(DisasContext *s, TCGv reg)
-{
- tcg_gen_mov_tl(cpu_cc_src2, reg);
- tcg_gen_mov_tl(cpu_cc_src, s->T1);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
-}
-
-static inline void gen_op_testl_T0_T1_cc(DisasContext *s)
-{
- tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1);
-}
-
-static void gen_op_update_neg_cc(DisasContext *s)
-{
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_neg_tl(cpu_cc_src, s->T0);
- tcg_gen_movi_tl(s->cc_srcT, 0);
-}
-
/* compute all eflags to reg */
static void gen_mov_eflags(DisasContext *s, TCGv reg)
{
@@ -923,94 +874,100 @@ typedef struct CCPrepare {
TCGv reg;
TCGv reg2;
target_ulong imm;
- target_ulong mask;
bool use_reg2;
bool no_setcond;
} CCPrepare;
-/* compute eflags.C to reg */
+static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size)
+{
+ if (size == MO_TL) {
+ return (CCPrepare) { .cond = TCG_COND_LT, .reg = src };
+ } else {
+ return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = src,
+ .imm = 1ull << ((8 << size) - 1) };
+ }
+}
+
+/* compute eflags.C, trying to store it in reg if not NULL */
static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg)
{
- TCGv t0, t1;
- int size, shift;
+ MemOp size;
switch (s->cc_op) {
case CC_OP_SUBB ... CC_OP_SUBQ:
/* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */
size = s->cc_op - CC_OP_SUBB;
- t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false);
- /* If no temporary was used, be careful not to alias t1 and t0. */
- t0 = t1 == cpu_cc_src ? s->tmp0 : reg;
- tcg_gen_mov_tl(t0, s->cc_srcT);
- gen_extu(size, t0);
- goto add_sub;
+ gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false);
+ gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+ return (CCPrepare) { .cond = TCG_COND_LTU, .reg = s->cc_srcT,
+ .reg2 = cpu_cc_src, .use_reg2 = true };
case CC_OP_ADDB ... CC_OP_ADDQ:
/* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */
size = s->cc_op - CC_OP_ADDB;
- t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false);
- t0 = gen_ext_tl(reg, cpu_cc_dst, size, false);
- add_sub:
- return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0,
- .reg2 = t1, .mask = -1, .use_reg2 = true };
+ gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size, false);
+ gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+ return (CCPrepare) { .cond = TCG_COND_LTU, .reg = cpu_cc_dst,
+ .reg2 = cpu_cc_src, .use_reg2 = true };
case CC_OP_LOGICB ... CC_OP_LOGICQ:
case CC_OP_CLR:
case CC_OP_POPCNT:
- return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_NEVER };
case CC_OP_INCB ... CC_OP_INCQ:
case CC_OP_DECB ... CC_OP_DECQ:
return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = -1, .no_setcond = true };
+ .no_setcond = true };
case CC_OP_SHLB ... CC_OP_SHLQ:
/* (CC_SRC >> (DATA_BITS - 1)) & 1 */
size = s->cc_op - CC_OP_SHLB;
- shift = (8 << size) - 1;
- return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = (target_ulong)1 << shift };
+ return gen_prepare_sign_nz(cpu_cc_src, size);
case CC_OP_MULB ... CC_OP_MULQ:
return (CCPrepare) { .cond = TCG_COND_NE,
- .reg = cpu_cc_src, .mask = -1 };
+ .reg = cpu_cc_src };
case CC_OP_BMILGB ... CC_OP_BMILGQ:
size = s->cc_op - CC_OP_BMILGB;
- t0 = gen_ext_tl(reg, cpu_cc_src, size, false);
- return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 };
+ gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+ return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src };
case CC_OP_ADCX:
case CC_OP_ADCOX:
return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst,
- .mask = -1, .no_setcond = true };
+ .no_setcond = true };
case CC_OP_EFLAGS:
case CC_OP_SARB ... CC_OP_SARQ:
/* CC_SRC & 1 */
- return (CCPrepare) { .cond = TCG_COND_NE,
- .reg = cpu_cc_src, .mask = CC_C };
+ return (CCPrepare) { .cond = TCG_COND_TSTNE,
+ .reg = cpu_cc_src, .imm = CC_C };
default:
/* The need to compute only C from CC_OP_DYNAMIC is important
in efficiently implementing e.g. INC at the start of a TB. */
gen_update_cc_op(s);
+ if (!reg) {
+ reg = tcg_temp_new();
+ }
gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src,
cpu_cc_src2, cpu_cc_op);
return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg,
- .mask = -1, .no_setcond = true };
+ .no_setcond = true };
}
}
-/* compute eflags.P to reg */
+/* compute eflags.P, trying to store it in reg if not NULL */
static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg)
{
gen_compute_eflags(s);
- return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = CC_P };
+ return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
+ .imm = CC_P };
}
-/* compute eflags.S to reg */
+/* compute eflags.S, trying to store it in reg if not NULL */
static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg)
{
switch (s->cc_op) {
@@ -1021,42 +978,40 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg)
case CC_OP_ADCX:
case CC_OP_ADOX:
case CC_OP_ADCOX:
- return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = CC_S };
+ return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
+ .imm = CC_S };
case CC_OP_CLR:
case CC_OP_POPCNT:
- return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_NEVER };
default:
{
MemOp 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 };
+ return gen_prepare_sign_nz(cpu_cc_dst, size);
}
}
}
-/* compute eflags.O to reg */
+/* compute eflags.O, trying to store it in reg if not NULL */
static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg)
{
switch (s->cc_op) {
case CC_OP_ADOX:
case CC_OP_ADCOX:
return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2,
- .mask = -1, .no_setcond = true };
+ .no_setcond = true };
case CC_OP_CLR:
case CC_OP_POPCNT:
- return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_NEVER };
case CC_OP_MULB ... CC_OP_MULQ:
- return (CCPrepare) { .cond = TCG_COND_NE,
- .reg = cpu_cc_src, .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src };
default:
gen_compute_eflags(s);
- return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = CC_O };
+ return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
+ .imm = CC_O };
}
}
-/* compute eflags.Z to reg */
+/* compute eflags.Z, trying to store it in reg if not NULL */
static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
{
switch (s->cc_op) {
@@ -1067,30 +1022,33 @@ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg)
case CC_OP_ADCX:
case CC_OP_ADOX:
case CC_OP_ADCOX:
- return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = CC_Z };
+ return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
+ .imm = CC_Z };
case CC_OP_CLR:
- return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_ALWAYS };
case CC_OP_POPCNT:
- return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src,
- .mask = -1 };
+ return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src };
default:
{
MemOp 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 };
+ if (size == MO_TL) {
+ return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst };
+ } else {
+ return (CCPrepare) { .cond = TCG_COND_TSTEQ, .reg = cpu_cc_dst,
+ .imm = (1ull << (8 << size)) - 1 };
+ }
}
}
}
-/* perform a conditional store into register 'reg' according to jump opcode
- value 'b'. In the fast case, T0 is guaranteed not to be used. */
+/* return how to compute jump opcode 'b'. 'reg' can be clobbered
+ * if needed; it may be used for CCPrepare.reg if that will
+ * provide more freedom in the translation of a subsequent setcond. */
static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
{
int inv, jcc_op, cond;
MemOp size;
CCPrepare cc;
- TCGv t0;
inv = b & 1;
jcc_op = (b >> 1) & 7;
@@ -1101,24 +1059,21 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
size = s->cc_op - CC_OP_SUBB;
switch (jcc_op) {
case JCC_BE:
- tcg_gen_mov_tl(s->tmp4, s->cc_srcT);
- gen_extu(size, s->tmp4);
- t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false);
- cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4,
- .reg2 = t0, .mask = -1, .use_reg2 = true };
+ gen_ext_tl(s->cc_srcT, s->cc_srcT, size, false);
+ gen_ext_tl(cpu_cc_src, cpu_cc_src, size, false);
+ cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->cc_srcT,
+ .reg2 = cpu_cc_src, .use_reg2 = true };
break;
-
case JCC_L:
cond = TCG_COND_LT;
goto fast_jcc_l;
case JCC_LE:
cond = TCG_COND_LE;
fast_jcc_l:
- tcg_gen_mov_tl(s->tmp4, s->cc_srcT);
- gen_exts(size, s->tmp4);
- t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true);
- cc = (CCPrepare) { .cond = cond, .reg = s->tmp4,
- .reg2 = t0, .mask = -1, .use_reg2 = true };
+ gen_ext_tl(s->cc_srcT, s->cc_srcT, size, true);
+ gen_ext_tl(cpu_cc_src, cpu_cc_src, size, true);
+ cc = (CCPrepare) { .cond = cond, .reg = s->cc_srcT,
+ .reg2 = cpu_cc_src, .use_reg2 = true };
break;
default:
@@ -1141,8 +1096,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
break;
case JCC_BE:
gen_compute_eflags(s);
- cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src,
- .mask = CC_Z | CC_C };
+ cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src,
+ .imm = CC_Z | CC_C };
break;
case JCC_S:
cc = gen_prepare_eflags_s(s, reg);
@@ -1152,22 +1107,22 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg)
break;
case JCC_L:
gen_compute_eflags(s);
- if (reg == cpu_cc_src) {
- reg = s->tmp0;
+ if (!reg || reg == cpu_cc_src) {
+ reg = tcg_temp_new();
}
tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S);
- cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg,
- .mask = CC_O };
+ cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg,
+ .imm = CC_O };
break;
default:
case JCC_LE:
gen_compute_eflags(s);
- if (reg == cpu_cc_src) {
- reg = s->tmp0;
+ if (!reg || reg == cpu_cc_src) {
+ reg = tcg_temp_new();
}
tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S);
- cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg,
- .mask = CC_O | CC_Z };
+ cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg,
+ .imm = CC_O | CC_Z };
break;
}
break;
@@ -1192,16 +1147,6 @@ static void gen_setcc1(DisasContext *s, int b, TCGv reg)
return;
}
- if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 &&
- cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) {
- tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask));
- tcg_gen_andi_tl(reg, reg, 1);
- return;
- }
- if (cc.mask != -1) {
- tcg_gen_andi_tl(reg, cc.reg, cc.mask);
- cc.reg = reg;
- }
if (cc.use_reg2) {
tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2);
} else {
@@ -1218,12 +1163,8 @@ static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg)
value 'b'. In the fast case, T0 is guaranteed not to be used. */
static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1)
{
- CCPrepare cc = gen_prepare_cc(s, b, s->T0);
+ CCPrepare cc = gen_prepare_cc(s, b, NULL);
- if (cc.mask != -1) {
- tcg_gen_andi_tl(s->T0, cc.reg, cc.mask);
- cc.reg = s->T0;
- }
if (cc.use_reg2) {
tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1);
} else {
@@ -1233,17 +1174,13 @@ static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1)
/* Generate a conditional jump to label 'l1' according to jump opcode
value 'b'. In the fast case, T0 is guaranteed not to be used.
- A translation block must end soon. */
+ One or both of the branches will call gen_jmp_rel, so ensure
+ cc_op is clean. */
static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1)
{
- CCPrepare cc = gen_prepare_cc(s, b, s->T0);
+ CCPrepare cc = gen_prepare_cc(s, b, NULL);
gen_update_cc_op(s);
- if (cc.mask != -1) {
- tcg_gen_andi_tl(s->T0, cc.reg, cc.mask);
- cc.reg = s->T0;
- }
- set_cc_op(s, CC_OP_DYNAMIC);
if (cc.use_reg2) {
tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1);
} else {
@@ -1252,11 +1189,15 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1)
}
/* XXX: does not work with gdbstub "ice" single step - not a
- serious problem */
+ serious problem. The caller can jump to the returned label
+ to stop the REP but, if the flags have changed, it has to call
+ gen_update_cc_op before doing so. */
static TCGLabel *gen_jz_ecx_string(DisasContext *s)
{
TCGLabel *l1 = gen_new_label();
TCGLabel *l2 = gen_new_label();
+
+ gen_update_cc_op(s);
gen_op_jnz_ecx(s, l1);
gen_set_label(l2);
gen_jmp_rel_csize(s, 0, 1);
@@ -1298,7 +1239,11 @@ static void gen_cmps(DisasContext *s, MemOp ot)
gen_string_movl_A0_EDI(s);
gen_op_ld_v(s, ot, s->T1, s->A0);
gen_string_movl_A0_ESI(s);
- gen_op(s, OP_CMPL, ot, OR_TMP0);
+ gen_op_ld_v(s, ot, s->T0, s->A0);
+ tcg_gen_mov_tl(cpu_cc_src, s->T1);
+ tcg_gen_mov_tl(s->cc_srcT, s->T0);
+ tcg_gen_sub_tl(cpu_cc_dst, s->T0, s->T1);
+ set_cc_op(s, CC_OP_SUBB + ot);
dshift = gen_compute_Dshift(s, ot);
gen_op_add_reg(s, s->aflag, R_ESI, dshift);
@@ -1352,7 +1297,6 @@ static void gen_repz(DisasContext *s, MemOp ot,
void (*fn)(DisasContext *s, MemOp ot))
{
TCGLabel *l2;
- gen_update_cc_op(s);
l2 = gen_jz_ecx_string(s);
fn(s, ot);
gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
@@ -1374,15 +1318,18 @@ static void gen_repz2(DisasContext *s, MemOp ot, int nz,
void (*fn)(DisasContext *s, MemOp ot))
{
TCGLabel *l2;
- gen_update_cc_op(s);
l2 = gen_jz_ecx_string(s);
fn(s, ot);
gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
- gen_update_cc_op(s);
gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2);
if (s->repz_opt) {
gen_op_jz_ecx(s, l2);
}
+ /*
+ * Only one iteration is done at a time, so the translation
+ * block ends unconditionally after this instruction and there
+ * is no control flow junction - no need to set CC_OP_DYNAMIC.
+ */
gen_jmp_rel_csize(s, -cur_insn_len(s), 0);
}
@@ -1485,165 +1432,6 @@ static bool check_cpl0(DisasContext *s)
return false;
}
-/* If vm86, check for iopl == 3; if not, raise #GP and return false. */
-static bool check_vm86_iopl(DisasContext *s)
-{
- if (!VM86(s) || IOPL(s) == 3) {
- return true;
- }
- gen_exception_gpf(s);
- return false;
-}
-
-/* Check for iopl allowing access; if not, raise #GP and return false. */
-static bool check_iopl(DisasContext *s)
-{
- if (VM86(s) ? IOPL(s) == 3 : CPL(s) <= IOPL(s)) {
- return true;
- }
- gen_exception_gpf(s);
- return false;
-}
-
-/* if d == OR_TMP0, it means memory operand (address in A0) */
-static void gen_op(DisasContext *s1, int op, MemOp ot, int d)
-{
- /* Invalid lock prefix when destination is not memory or OP_CMPL. */
- if ((d != OR_TMP0 || op == OP_CMPL) && s1->prefix & PREFIX_LOCK) {
- gen_illegal_opcode(s1);
- return;
- }
-
- if (d != OR_TMP0) {
- gen_op_mov_v_reg(s1, ot, s1->T0, d);
- } else if (!(s1->prefix & PREFIX_LOCK)) {
- gen_op_ld_v(s1, ot, s1->T0, s1->A0);
- }
- switch(op) {
- case OP_ADCL:
- gen_compute_eflags_c(s1, s1->tmp4);
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1);
- tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_add_tl(s1->T0, s1->T0, s1->T1);
- tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update3_cc(s1, s1->tmp4);
- set_cc_op(s1, CC_OP_ADCB + ot);
- break;
- case OP_SBBL:
- gen_compute_eflags_c(s1, s1->tmp4);
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4);
- tcg_gen_neg_tl(s1->T0, s1->T0);
- tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
- tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update3_cc(s1, s1->tmp4);
- set_cc_op(s1, CC_OP_SBBB + ot);
- break;
- case OP_ADDL:
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_add_tl(s1->T0, s1->T0, s1->T1);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update2_cc(s1);
- set_cc_op(s1, CC_OP_ADDB + ot);
- break;
- case OP_SUBL:
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_neg_tl(s1->T0, s1->T1);
- tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
- s1->mem_index, ot | MO_LE);
- tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
- } else {
- tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
- tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update2_cc(s1);
- set_cc_op(s1, CC_OP_SUBB + ot);
- break;
- default:
- case OP_ANDL:
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_and_tl(s1->T0, s1->T0, s1->T1);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update1_cc(s1);
- set_cc_op(s1, CC_OP_LOGICB + ot);
- break;
- case OP_ORL:
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_or_tl(s1->T0, s1->T0, s1->T1);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update1_cc(s1);
- set_cc_op(s1, CC_OP_LOGICB + ot);
- break;
- case OP_XORL:
- if (s1->prefix & PREFIX_LOCK) {
- tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1,
- s1->mem_index, ot | MO_LE);
- } else {
- tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1);
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
- gen_op_update1_cc(s1);
- set_cc_op(s1, CC_OP_LOGICB + ot);
- break;
- case OP_CMPL:
- tcg_gen_mov_tl(cpu_cc_src, s1->T1);
- tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
- tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
- set_cc_op(s1, CC_OP_SUBB + ot);
- break;
- }
-}
-
-/* if d == OR_TMP0, it means memory operand (address in A0) */
-static void gen_inc(DisasContext *s1, MemOp ot, int d, int c)
-{
- if (s1->prefix & PREFIX_LOCK) {
- if (d != OR_TMP0) {
- /* Lock prefix when destination is not memory */
- gen_illegal_opcode(s1);
- return;
- }
- tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1);
- tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0,
- s1->mem_index, ot | MO_LE);
- } else {
- if (d != OR_TMP0) {
- gen_op_mov_v_reg(s1, ot, s1->T0, d);
- } else {
- gen_op_ld_v(s1, ot, s1->T0, s1->A0);
- }
- tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1));
- gen_op_st_rm_T0_A0(s1, ot, d);
- }
-
- gen_compute_eflags_c(s1, cpu_cc_src);
- tcg_gen_mov_tl(cpu_cc_dst, s1->T0);
- set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot);
-}
-
static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result,
TCGv shm1, TCGv count, bool is_right)
{
@@ -1686,298 +1474,6 @@ static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result,
set_cc_op(s, CC_OP_DYNAMIC);
}
-static void gen_shift_rm_T1(DisasContext *s, MemOp ot, int op1,
- int is_right, int is_arith)
-{
- target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f);
-
- /* load */
- if (op1 == OR_TMP0) {
- gen_op_ld_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, op1);
- }
-
- tcg_gen_andi_tl(s->T1, s->T1, mask);
- tcg_gen_subi_tl(s->tmp0, s->T1, 1);
-
- if (is_right) {
- if (is_arith) {
- gen_exts(ot, s->T0);
- tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0);
- tcg_gen_sar_tl(s->T0, s->T0, s->T1);
- } else {
- gen_extu(ot, s->T0);
- tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0);
- tcg_gen_shr_tl(s->T0, s->T0, s->T1);
- }
- } else {
- tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0);
- tcg_gen_shl_tl(s->T0, s->T0, s->T1);
- }
-
- /* store */
- gen_op_st_rm_T0_A0(s, ot, op1);
-
- gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right);
-}
-
-static void gen_shift_rm_im(DisasContext *s, MemOp ot, int op1, int op2,
- int is_right, int is_arith)
-{
- int mask = (ot == MO_64 ? 0x3f : 0x1f);
-
- /* load */
- if (op1 == OR_TMP0)
- gen_op_ld_v(s, ot, s->T0, s->A0);
- else
- gen_op_mov_v_reg(s, ot, s->T0, op1);
-
- op2 &= mask;
- if (op2 != 0) {
- if (is_right) {
- if (is_arith) {
- gen_exts(ot, s->T0);
- tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1);
- tcg_gen_sari_tl(s->T0, s->T0, op2);
- } else {
- gen_extu(ot, s->T0);
- tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1);
- tcg_gen_shri_tl(s->T0, s->T0, op2);
- }
- } else {
- tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1);
- tcg_gen_shli_tl(s->T0, s->T0, op2);
- }
- }
-
- /* store */
- 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, s->tmp4);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot);
- }
-}
-
-static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right)
-{
- target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f);
- TCGv_i32 t0, t1;
-
- /* load */
- if (op1 == OR_TMP0) {
- gen_op_ld_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, op1);
- }
-
- tcg_gen_andi_tl(s->T1, s->T1, mask);
-
- switch (ot) {
- case MO_8:
- /* Replicate the 8-bit input so that a 32-bit rotate works. */
- tcg_gen_ext8u_tl(s->T0, s->T0);
- tcg_gen_muli_tl(s->T0, s->T0, 0x01010101);
- goto do_long;
- case MO_16:
- /* Replicate the 16-bit input so that a 32-bit rotate works. */
- tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16);
- goto do_long;
- do_long:
-#ifdef TARGET_X86_64
- case MO_32:
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1);
- if (is_right) {
- tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32);
- } else {
- tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32);
- }
- tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32);
- break;
-#endif
- default:
- if (is_right) {
- tcg_gen_rotr_tl(s->T0, s->T0, s->T1);
- } else {
- tcg_gen_rotl_tl(s->T0, s->T0, s->T1);
- }
- break;
- }
-
- /* store */
- gen_op_st_rm_T0_A0(s, ot, op1);
-
- /* We'll need the flags computed into CC_SRC. */
- gen_compute_eflags(s);
-
- /* The value that was "rotated out" is now present at the other end
- of the word. Compute C into CC_DST and O into CC_SRC2. Note that
- since we've computed the flags into CC_SRC, these variables are
- currently dead. */
- if (is_right) {
- tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1);
- tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask);
- tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1);
- } else {
- tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask);
- tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1);
- }
- tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1);
- tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst);
-
- /* Now conditionally store the new CC_OP value. If the shift count
- is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live.
- Otherwise reuse CC_OP_ADCOX which have the C and O flags split out
- exactly as we computed above. */
- t0 = tcg_constant_i32(0);
- t1 = tcg_temp_new_i32();
- tcg_gen_trunc_tl_i32(t1, s->T1);
- tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX);
- tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS);
- tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0,
- s->tmp2_i32, s->tmp3_i32);
-
- /* The CC_OP value is no longer predictable. */
- set_cc_op(s, CC_OP_DYNAMIC);
-}
-
-static void gen_rot_rm_im(DisasContext *s, MemOp ot, int op1, int op2,
- int is_right)
-{
- int mask = (ot == MO_64 ? 0x3f : 0x1f);
- int shift;
-
- /* load */
- if (op1 == OR_TMP0) {
- gen_op_ld_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, op1);
- }
-
- op2 &= mask;
- if (op2 != 0) {
- switch (ot) {
-#ifdef TARGET_X86_64
- case MO_32:
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- if (is_right) {
- tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2);
- } else {
- tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2);
- }
- tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32);
- break;
-#endif
- default:
- if (is_right) {
- tcg_gen_rotri_tl(s->T0, s->T0, op2);
- } else {
- tcg_gen_rotli_tl(s->T0, s->T0, op2);
- }
- break;
- case MO_8:
- mask = 7;
- goto do_shifts;
- case MO_16:
- mask = 15;
- do_shifts:
- shift = op2 & mask;
- if (is_right) {
- shift = mask + 1 - shift;
- }
- gen_extu(ot, s->T0);
- tcg_gen_shli_tl(s->tmp0, s->T0, shift);
- tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift);
- tcg_gen_or_tl(s->T0, s->T0, s->tmp0);
- break;
- }
- }
-
- /* store */
- gen_op_st_rm_T0_A0(s, ot, op1);
-
- if (op2 != 0) {
- /* Compute the flags into CC_SRC. */
- gen_compute_eflags(s);
-
- /* The value that was "rotated out" is now present at the other end
- of the word. Compute C into CC_DST and O into CC_SRC2. Note that
- since we've computed the flags into CC_SRC, these variables are
- currently dead. */
- if (is_right) {
- tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1);
- tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask);
- tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1);
- } else {
- tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask);
- tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1);
- }
- tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1);
- tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst);
- set_cc_op(s, CC_OP_ADCOX);
- }
-}
-
-/* XXX: add faster immediate = 1 case */
-static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1,
- int is_right)
-{
- gen_compute_eflags(s);
- assert(s->cc_op == CC_OP_EFLAGS);
-
- /* load */
- if (op1 == OR_TMP0)
- gen_op_ld_v(s, ot, s->T0, s->A0);
- else
- gen_op_mov_v_reg(s, ot, s->T0, op1);
-
- if (is_right) {
- switch (ot) {
- case MO_8:
- gen_helper_rcrb(s->T0, tcg_env, s->T0, s->T1);
- break;
- case MO_16:
- gen_helper_rcrw(s->T0, tcg_env, s->T0, s->T1);
- break;
- case MO_32:
- gen_helper_rcrl(s->T0, tcg_env, s->T0, s->T1);
- break;
-#ifdef TARGET_X86_64
- case MO_64:
- gen_helper_rcrq(s->T0, tcg_env, s->T0, s->T1);
- break;
-#endif
- default:
- g_assert_not_reached();
- }
- } else {
- switch (ot) {
- case MO_8:
- gen_helper_rclb(s->T0, tcg_env, s->T0, s->T1);
- break;
- case MO_16:
- gen_helper_rclw(s->T0, tcg_env, s->T0, s->T1);
- break;
- case MO_32:
- gen_helper_rcll(s->T0, tcg_env, s->T0, s->T1);
- break;
-#ifdef TARGET_X86_64
- case MO_64:
- gen_helper_rclq(s->T0, tcg_env, s->T0, s->T1);
- break;
-#endif
- default:
- g_assert_not_reached();
- }
- }
- /* store */
- gen_op_st_rm_T0_A0(s, ot, op1);
-}
-
/* XXX: add faster immediate case */
static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1,
bool is_right, TCGv count_in)
@@ -2062,63 +1558,6 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1,
gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right);
}
-static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s)
-{
- if (s != OR_TMP1)
- gen_op_mov_v_reg(s1, ot, s1->T1, s);
- switch(op) {
- case OP_ROL:
- gen_rot_rm_T1(s1, ot, d, 0);
- break;
- case OP_ROR:
- gen_rot_rm_T1(s1, ot, d, 1);
- break;
- case OP_SHL:
- case OP_SHL1:
- gen_shift_rm_T1(s1, ot, d, 0, 0);
- break;
- case OP_SHR:
- gen_shift_rm_T1(s1, ot, d, 1, 0);
- break;
- case OP_SAR:
- gen_shift_rm_T1(s1, ot, d, 1, 1);
- break;
- case OP_RCL:
- gen_rotc_rm_T1(s1, ot, d, 0);
- break;
- case OP_RCR:
- gen_rotc_rm_T1(s1, ot, d, 1);
- break;
- }
-}
-
-static void gen_shifti(DisasContext *s1, int op, MemOp ot, int d, int c)
-{
- switch(op) {
- case OP_ROL:
- gen_rot_rm_im(s1, ot, d, c, 0);
- break;
- case OP_ROR:
- gen_rot_rm_im(s1, ot, d, c, 1);
- break;
- case OP_SHL:
- case OP_SHL1:
- gen_shift_rm_im(s1, ot, d, c, 0, 0);
- break;
- case OP_SHR:
- gen_shift_rm_im(s1, ot, d, c, 1, 0);
- break;
- case OP_SAR:
- gen_shift_rm_im(s1, ot, d, c, 1, 1);
- break;
- default:
- /* currently not optimized */
- tcg_gen_movi_tl(s1->T1, c);
- gen_shift(s1, op, ot, d, OR_TMP1);
- break;
- }
-}
-
#define X86_MAX_INSN_LENGTH 15
static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes)
@@ -2139,9 +1578,8 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes)
* This can happen even if the operand is only one byte long!
*/
if (((s->pc - 1) ^ (pc - 1)) & TARGET_PAGE_MASK) {
- volatile uint8_t unused =
- cpu_ldub_code(env, (s->pc - 1) & TARGET_PAGE_MASK);
- (void) unused;
+ (void)translator_ldub(env, &s->base,
+ (s->pc - 1) & TARGET_PAGE_MASK);
}
siglongjmp(s->jmpbuf, 1);
}
@@ -2154,11 +1592,6 @@ static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s)
return translator_ldub(env, &s->base, advance_pc(env, s, 1));
}
-static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s)
-{
- return translator_lduw(env, &s->base, advance_pc(env, s, 2));
-}
-
static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s)
{
return translator_lduw(env, &s->base, advance_pc(env, s, 2));
@@ -2484,13 +1917,16 @@ static target_long insn_get_signed(CPUX86State *env, DisasContext *s, MemOp ot)
return ret;
}
-static inline int insn_const_size(MemOp ot)
+static void gen_conditional_jump_labels(DisasContext *s, target_long diff,
+ TCGLabel *not_taken, TCGLabel *taken)
{
- if (ot <= MO_32) {
- return 1 << ot;
- } else {
- return 4;
+ if (not_taken) {
+ gen_set_label(not_taken);
}
+ gen_jmp_rel_csize(s, 0, 1);
+
+ gen_set_label(taken);
+ gen_jmp_rel(s, s->dflag, diff, 0);
}
static void gen_jcc(DisasContext *s, int b, int diff)
@@ -2498,20 +1934,13 @@ static void gen_jcc(DisasContext *s, int b, int diff)
TCGLabel *l1 = gen_new_label();
gen_jcc1(s, b, l1);
- gen_jmp_rel_csize(s, 0, 1);
- gen_set_label(l1);
- gen_jmp_rel(s, s->dflag, diff, 0);
+ gen_conditional_jump_labels(s, diff, NULL, l1);
}
static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src)
{
- CCPrepare cc = gen_prepare_cc(s, b, s->T1);
+ CCPrepare cc = gen_prepare_cc(s, b, NULL);
- if (cc.mask != -1) {
- TCGv t0 = tcg_temp_new();
- tcg_gen_andi_tl(t0, cc.reg, cc.mask);
- cc.reg = t0;
- }
if (!cc.use_reg2) {
cc.reg2 = tcg_constant_tl(cc.imm);
}
@@ -2519,26 +1948,21 @@ static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src)
tcg_gen_movcond_tl(cc.cond, dest, cc.reg, cc.reg2, src, dest);
}
-static inline void gen_op_movl_T0_seg(DisasContext *s, X86Seg seg_reg)
-{
- tcg_gen_ld32u_tl(s->T0, tcg_env,
- offsetof(CPUX86State,segs[seg_reg].selector));
-}
-
-static inline void gen_op_movl_seg_T0_vm(DisasContext *s, X86Seg seg_reg)
+static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg)
{
- tcg_gen_ext16u_tl(s->T0, s->T0);
- tcg_gen_st32_tl(s->T0, tcg_env,
+ TCGv selector = tcg_temp_new();
+ tcg_gen_ext16u_tl(selector, seg);
+ tcg_gen_st32_tl(selector, tcg_env,
offsetof(CPUX86State,segs[seg_reg].selector));
- tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4);
+ tcg_gen_shli_tl(cpu_seg_base[seg_reg], selector, 4);
}
-/* move T0 to seg_reg and compute if the CPU state may change. Never
+/* move SRC to seg_reg and compute if the CPU state may change. Never
call this function with seg_reg == R_CS */
-static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg)
+static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src)
{
if (PE(s) && !VM86(s)) {
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
+ tcg_gen_trunc_tl_i32(s->tmp2_i32, src);
gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), s->tmp2_i32);
/* abort translation because the addseg value may change or
because ss32 may change. For R_SS, translation must always
@@ -2550,13 +1974,45 @@ static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg)
s->base.is_jmp = DISAS_EOB_NEXT;
}
} else {
- gen_op_movl_seg_T0_vm(s, seg_reg);
+ gen_op_movl_seg_real(s, seg_reg, src);
if (seg_reg == R_SS) {
s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ;
}
}
}
+static void gen_far_call(DisasContext *s)
+{
+ TCGv_i32 new_cs = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(new_cs, s->T1);
+ if (PE(s) && !VM86(s)) {
+ gen_helper_lcall_protected(tcg_env, new_cs, s->T0,
+ tcg_constant_i32(s->dflag - 1),
+ eip_next_tl(s));
+ } else {
+ TCGv_i32 new_eip = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(new_eip, s->T0);
+ gen_helper_lcall_real(tcg_env, new_cs, new_eip,
+ tcg_constant_i32(s->dflag - 1),
+ eip_next_i32(s));
+ }
+ s->base.is_jmp = DISAS_JUMP;
+}
+
+static void gen_far_jmp(DisasContext *s)
+{
+ if (PE(s) && !VM86(s)) {
+ TCGv_i32 new_cs = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(new_cs, s->T1);
+ gen_helper_ljmp_protected(tcg_env, new_cs, s->T0,
+ eip_next_tl(s));
+ } else {
+ gen_op_movl_seg_real(s, R_CS, s->T1);
+ gen_op_jmp_v(s, s->T0);
+ }
+ s->base.is_jmp = DISAS_JUMP;
+}
+
static void gen_svm_check_intercept(DisasContext *s, uint32_t type)
{
/* no SVM activated; fast case */
@@ -2719,7 +2175,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s)
fprintf(logfile, "ILLOPC: " TARGET_FMT_lx ":", pc);
for (; pc < end; ++pc) {
- fprintf(logfile, " %02x", cpu_ldub_code(env, pc));
+ fprintf(logfile, " %02x", translator_ldub(env, &s->base, pc));
}
fprintf(logfile, "\n");
qemu_log_unlock(logfile);
@@ -2729,7 +2185,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s)
/* an interrupt is different from an exception because of the
privilege checks */
-static void gen_interrupt(DisasContext *s, int intno)
+static void gen_interrupt(DisasContext *s, uint8_t intno)
{
gen_update_cc_op(s);
gen_update_eip_cur(s);
@@ -2796,15 +2252,19 @@ static void gen_bnd_jmp(DisasContext *s)
If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of
S->TF. This is used by the syscall/sysret insns. */
static void
-do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr)
+gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr)
{
+ bool inhibit_reset;
+
gen_update_cc_op(s);
/* If several instructions disable interrupts, only the first does it. */
- if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) {
- gen_set_hflag(s, HF_INHIBIT_IRQ_MASK);
- } else {
+ inhibit_reset = false;
+ if (s->flags & HF_INHIBIT_IRQ_MASK) {
gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK);
+ inhibit_reset = true;
+ } else if (inhibit) {
+ gen_set_hflag(s, HF_INHIBIT_IRQ_MASK);
}
if (s->base.tb->flags & HF_RF_MASK) {
@@ -2815,7 +2275,9 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr)
tcg_gen_exit_tb(NULL, 0);
} else if (s->flags & HF_TF_MASK) {
gen_helper_single_step(tcg_env);
- } else if (jr) {
+ } else if (jr &&
+ /* give irqs a chance to happen */
+ !inhibit_reset) {
tcg_gen_lookup_and_goto_ptr();
} else {
tcg_gen_exit_tb(NULL, 0);
@@ -2824,28 +2286,27 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr)
}
static inline void
-gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf)
+gen_eob_syscall(DisasContext *s)
{
- do_gen_eob_worker(s, inhibit, recheck_tf, false);
+ gen_eob_worker(s, false, true, false);
}
-/* End of block.
- If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. */
-static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit)
+/* End of block. Set HF_INHIBIT_IRQ_MASK if it isn't already set. */
+static void gen_eob_inhibit_irq(DisasContext *s)
{
- gen_eob_worker(s, inhibit, false);
+ gen_eob_worker(s, true, false, false);
}
/* End of block, resetting the inhibit irq flag. */
static void gen_eob(DisasContext *s)
{
- gen_eob_worker(s, false, false);
+ gen_eob_worker(s, false, false, false);
}
/* Jump to register */
static void gen_jr(DisasContext *s)
{
- do_gen_eob_worker(s, false, false, true);
+ gen_eob_worker(s, false, false, true);
}
/* Jump to eip+diff, truncating the result to OT. */
@@ -2856,6 +2317,8 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num)
target_ulong new_pc = s->pc + diff;
target_ulong new_eip = new_pc - s->cs_base;
+ assert(!s->cc_op_dirty);
+
/* In 64-bit mode, operand size is fixed at 64 bits. */
if (!CODE64(s)) {
if (ot == MO_16) {
@@ -2869,9 +2332,6 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num)
}
new_eip &= mask;
- gen_update_cc_op(s);
- set_cc_op(s, CC_OP_DYNAMIC);
-
if (tb_cflags(s->base.tb) & CF_PCREL) {
tcg_gen_addi_tl(cpu_eip, cpu_eip, new_pc - s->pc_save);
/*
@@ -2978,10 +2438,6 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align)
tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop);
}
-#include "decode-new.h"
-#include "emit.c.inc"
-#include "decode-new.c.inc"
-
static void gen_cmpxchg8b(DisasContext *s, CPUX86State *env, int modrm)
{
TCGv_i64 cmp, val, old;
@@ -3080,739 +2536,583 @@ static void gen_cmpxchg16b(DisasContext *s, CPUX86State *env, int modrm)
}
#endif
-/* convert one instruction. s->base.is_jmp is set if the translation must
- be stopped. Return the next pc value */
-static bool disas_insn(DisasContext *s, CPUState *cpu)
+static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b)
{
CPUX86State *env = cpu_env(cpu);
- int b, prefixes;
- int shift;
- MemOp ot, aflag, dflag;
- int modrm, reg, rm, mod, op, opreg, val;
- bool orig_cc_op_dirty = s->cc_op_dirty;
- CCOp orig_cc_op = s->cc_op;
- target_ulong orig_pc_save = s->pc_save;
+ bool update_fip = true;
+ int modrm, mod, rm, op;
- s->pc = s->base.pc_next;
- s->override = -1;
-#ifdef TARGET_X86_64
- s->rex_r = 0;
- s->rex_x = 0;
- s->rex_b = 0;
-#endif
- s->rip_offset = 0; /* for relative ip address */
- s->vex_l = 0;
- s->vex_v = 0;
- s->vex_w = false;
- switch (sigsetjmp(s->jmpbuf, 0)) {
- case 0:
- break;
- case 1:
- gen_exception_gpf(s);
+ if (s->flags & (HF_EM_MASK | HF_TS_MASK)) {
+ /* if CR0.EM or CR0.TS are set, generate an FPU exception */
+ /* XXX: what to do if illegal op ? */
+ gen_exception(s, EXCP07_PREX);
return true;
- case 2:
- /* Restore state that may affect the next instruction. */
- s->pc = s->base.pc_next;
- /*
- * TODO: These save/restore can be removed after the table-based
- * decoder is complete; we will be decoding the insn completely
- * before any code generation that might affect these variables.
- */
- s->cc_op_dirty = orig_cc_op_dirty;
- s->cc_op = orig_cc_op;
- s->pc_save = orig_pc_save;
- /* END TODO */
- s->base.num_insns--;
- tcg_remove_ops_after(s->prev_insn_end);
- s->base.insn_start = s->prev_insn_start;
- s->base.is_jmp = DISAS_TOO_MANY;
- return false;
- default:
- g_assert_not_reached();
}
+ modrm = x86_ldub_code(env, s);
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ op = ((b & 7) << 3) | ((modrm >> 3) & 7);
+ if (mod != 3) {
+ /* memory op */
+ AddressParts a = gen_lea_modrm_0(env, s, modrm);
+ TCGv ea = gen_lea_modrm_1(s, a, false);
+ TCGv last_addr = tcg_temp_new();
+ bool update_fdp = true;
+
+ tcg_gen_mov_tl(last_addr, ea);
+ gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override);
+
+ switch (op) {
+ case 0x00 ... 0x07: /* fxxxs */
+ case 0x10 ... 0x17: /* fixxxl */
+ case 0x20 ... 0x27: /* fxxxl */
+ case 0x30 ... 0x37: /* fixxx */
+ {
+ int op1;
+ op1 = op & 7;
+
+ switch (op >> 4) {
+ case 0:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ gen_helper_flds_FT0(tcg_env, s->tmp2_i32);
+ break;
+ case 1:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ gen_helper_fildl_FT0(tcg_env, s->tmp2_i32);
+ break;
+ case 2:
+ tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ gen_helper_fldl_FT0(tcg_env, s->tmp1_i64);
+ break;
+ case 3:
+ default:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LESW);
+ gen_helper_fildl_FT0(tcg_env, s->tmp2_i32);
+ break;
+ }
- prefixes = 0;
-
- next_byte:
- s->prefix = prefixes;
- b = x86_ldub_code(env, s);
- /* Collect prefixes. */
- switch (b) {
- default:
- break;
- case 0x0f:
- b = x86_ldub_code(env, s) + 0x100;
- break;
- case 0xf3:
- prefixes |= PREFIX_REPZ;
- prefixes &= ~PREFIX_REPNZ;
- goto next_byte;
- case 0xf2:
- prefixes |= PREFIX_REPNZ;
- prefixes &= ~PREFIX_REPZ;
- goto next_byte;
- case 0xf0:
- prefixes |= PREFIX_LOCK;
- goto next_byte;
- case 0x2e:
- s->override = R_CS;
- goto next_byte;
- case 0x36:
- s->override = R_SS;
- goto next_byte;
- case 0x3e:
- s->override = R_DS;
- goto next_byte;
- case 0x26:
- s->override = R_ES;
- goto next_byte;
- case 0x64:
- s->override = R_FS;
- goto next_byte;
- case 0x65:
- s->override = R_GS;
- goto next_byte;
- case 0x66:
- prefixes |= PREFIX_DATA;
- goto next_byte;
- case 0x67:
- prefixes |= PREFIX_ADR;
- goto next_byte;
-#ifdef TARGET_X86_64
- case 0x40 ... 0x4f:
- if (CODE64(s)) {
- /* REX prefix */
- prefixes |= PREFIX_REX;
- s->vex_w = (b >> 3) & 1;
- s->rex_r = (b & 0x4) << 1;
- s->rex_x = (b & 0x2) << 2;
- s->rex_b = (b & 0x1) << 3;
- goto next_byte;
- }
- break;
-#endif
- case 0xc5: /* 2-byte VEX */
- case 0xc4: /* 3-byte VEX */
- if (CODE32(s) && !VM86(s)) {
- int vex2 = x86_ldub_code(env, s);
- s->pc--; /* rewind the advance_pc() x86_ldub_code() did */
-
- if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) {
- /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b,
- otherwise the instruction is LES or LDS. */
- break;
+ gen_helper_fp_arith_ST0_FT0(op1);
+ if (op1 == 3) {
+ /* fcomp needs pop */
+ gen_helper_fpop(tcg_env);
+ }
}
- disas_insn_new(s, cpu, b);
- return s->pc;
- }
- break;
- }
-
- /* Post-process prefixes. */
- if (CODE64(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(s) ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32);
- /* In 64-bit mode, 0x67 selects 32-bit addressing. */
- aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64);
- } else {
- /* In 16/32-bit mode, 0x66 selects the opposite data size. */
- if (CODE32(s) ^ ((prefixes & PREFIX_DATA) != 0)) {
- dflag = MO_32;
- } else {
- dflag = MO_16;
- }
- /* In 16/32-bit mode, 0x67 selects the opposite addressing. */
- if (CODE32(s) ^ ((prefixes & PREFIX_ADR) != 0)) {
- aflag = MO_32;
- } else {
- aflag = MO_16;
- }
- }
-
- s->prefix = prefixes;
- s->aflag = aflag;
- s->dflag = dflag;
-
- /* now check op code */
- switch (b) {
- /**************************/
- /* arith & logic */
- case 0x00 ... 0x05:
- case 0x08 ... 0x0d:
- case 0x10 ... 0x15:
- case 0x18 ... 0x1d:
- case 0x20 ... 0x25:
- case 0x28 ... 0x2d:
- case 0x30 ... 0x35:
- case 0x38 ... 0x3d:
- {
- int f;
- op = (b >> 3) & 7;
- f = (b >> 1) & 3;
-
- ot = mo_b_d(b, dflag);
-
- switch(f) {
- case 0: /* OP Ev, Gv */
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
- if (mod != 3) {
- 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);
- tcg_gen_movi_tl(s->T0, 0);
- gen_op_mov_reg_v(s, ot, reg, s->T0);
+ break;
+ case 0x08: /* flds */
+ case 0x0a: /* fsts */
+ case 0x0b: /* fstps */
+ case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */
+ case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */
+ case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */
+ switch (op & 7) {
+ case 0:
+ switch (op >> 4) {
+ case 0:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ gen_helper_flds_ST0(tcg_env, s->tmp2_i32);
+ break;
+ case 1:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ gen_helper_fildl_ST0(tcg_env, s->tmp2_i32);
+ break;
+ case 2:
+ tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ gen_helper_fldl_ST0(tcg_env, s->tmp1_i64);
+ break;
+ case 3:
+ default:
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LESW);
+ gen_helper_fildl_ST0(tcg_env, s->tmp2_i32);
break;
- } else {
- opreg = rm;
}
- gen_op_mov_v_reg(s, ot, s->T1, reg);
- gen_op(s, op, ot, opreg);
break;
- case 1: /* OP Gv, Ev */
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- reg = ((modrm >> 3) & 7) | REX_R(s);
- rm = (modrm & 7) | REX_B(s);
- if (mod != 3) {
- gen_lea_modrm(env, s, modrm);
- gen_op_ld_v(s, ot, s->T1, s->A0);
- } else if (op == OP_XORL && rm == reg) {
- goto xor_zero;
- } else {
- gen_op_mov_v_reg(s, ot, s->T1, rm);
+ case 1:
+ /* XXX: the corresponding CPUID bit must be tested ! */
+ switch (op >> 4) {
+ case 1:
+ gen_helper_fisttl_ST0(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ break;
+ case 2:
+ gen_helper_fisttll_ST0(s->tmp1_i64, tcg_env);
+ tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ break;
+ case 3:
+ default:
+ gen_helper_fistt_ST0(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUW);
+ break;
}
- gen_op(s, op, ot, reg);
- break;
- case 2: /* OP A, Iv */
- val = insn_get(env, s, ot);
- tcg_gen_movi_tl(s->T1, val);
- gen_op(s, op, ot, OR_EAX);
+ gen_helper_fpop(tcg_env);
break;
- }
- }
- break;
-
- case 0x82:
- if (CODE64(s))
- goto illegal_op;
- /* fall through */
- case 0x80: /* GRP1 */
- case 0x81:
- case 0x83:
- {
- ot = mo_b_d(b, dflag);
-
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
- op = (modrm >> 3) & 7;
-
- if (mod != 3) {
- if (b == 0x83)
- s->rip_offset = 1;
- else
- s->rip_offset = insn_const_size(ot);
- gen_lea_modrm(env, s, modrm);
- opreg = OR_TMP0;
- } else {
- opreg = rm;
- }
-
- switch(b) {
default:
- case 0x80:
- case 0x81:
- case 0x82:
- val = insn_get(env, s, ot);
- break;
- case 0x83:
- val = (int8_t)insn_get(env, s, MO_8);
+ switch (op >> 4) {
+ case 0:
+ gen_helper_fsts_ST0(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ break;
+ case 1:
+ gen_helper_fistl_ST0(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUL);
+ break;
+ case 2:
+ gen_helper_fstl_ST0(s->tmp1_i64, tcg_env);
+ tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ break;
+ case 3:
+ default:
+ gen_helper_fist_ST0(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUW);
+ break;
+ }
+ if ((op & 7) == 3) {
+ gen_helper_fpop(tcg_env);
+ }
break;
}
- tcg_gen_movi_tl(s->T1, val);
- gen_op(s, op, ot, opreg);
+ break;
+ case 0x0c: /* fldenv mem */
+ gen_helper_fldenv(tcg_env, s->A0,
+ tcg_constant_i32(s->dflag - 1));
+ update_fip = update_fdp = false;
+ break;
+ case 0x0d: /* fldcw mem */
+ tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUW);
+ gen_helper_fldcw(tcg_env, s->tmp2_i32);
+ update_fip = update_fdp = false;
+ break;
+ case 0x0e: /* fnstenv mem */
+ gen_helper_fstenv(tcg_env, s->A0,
+ tcg_constant_i32(s->dflag - 1));
+ update_fip = update_fdp = false;
+ break;
+ case 0x0f: /* fnstcw mem */
+ gen_helper_fnstcw(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUW);
+ update_fip = update_fdp = false;
+ break;
+ case 0x1d: /* fldt mem */
+ gen_helper_fldt_ST0(tcg_env, s->A0);
+ break;
+ case 0x1f: /* fstpt mem */
+ gen_helper_fstt_ST0(tcg_env, s->A0);
+ gen_helper_fpop(tcg_env);
+ break;
+ case 0x2c: /* frstor mem */
+ gen_helper_frstor(tcg_env, s->A0,
+ tcg_constant_i32(s->dflag - 1));
+ update_fip = update_fdp = false;
+ break;
+ case 0x2e: /* fnsave mem */
+ gen_helper_fsave(tcg_env, s->A0,
+ tcg_constant_i32(s->dflag - 1));
+ update_fip = update_fdp = false;
+ break;
+ case 0x2f: /* fnstsw mem */
+ gen_helper_fnstsw(s->tmp2_i32, tcg_env);
+ tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
+ s->mem_index, MO_LEUW);
+ update_fip = update_fdp = false;
+ break;
+ case 0x3c: /* fbld */
+ gen_helper_fbld_ST0(tcg_env, s->A0);
+ break;
+ case 0x3e: /* fbstp */
+ gen_helper_fbst_ST0(tcg_env, s->A0);
+ gen_helper_fpop(tcg_env);
+ break;
+ case 0x3d: /* fildll */
+ tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ gen_helper_fildll_ST0(tcg_env, s->tmp1_i64);
+ break;
+ case 0x3f: /* fistpll */
+ gen_helper_fistll_ST0(s->tmp1_i64, tcg_env);
+ tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
+ s->mem_index, MO_LEUQ);
+ gen_helper_fpop(tcg_env);
+ break;
+ default:
+ return false;
}
- break;
- /**************************/
- /* inc, dec, and other misc arith */
- case 0x40 ... 0x47: /* inc Gv */
- ot = dflag;
- gen_inc(s, ot, OR_EAX + (b & 7), 1);
- break;
- case 0x48 ... 0x4f: /* dec Gv */
- ot = dflag;
- gen_inc(s, ot, OR_EAX + (b & 7), -1);
- break;
- case 0xf6: /* GRP3 */
- case 0xf7:
- ot = mo_b_d(b, dflag);
+ if (update_fdp) {
+ int last_seg = s->override >= 0 ? s->override : a.def_seg;
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
- op = (modrm >> 3) & 7;
- if (mod != 3) {
- if (op == 0) {
- s->rip_offset = insn_const_size(ot);
- }
- gen_lea_modrm(env, s, modrm);
- /* For those below that handle locked memory, don't load here. */
- if (!(s->prefix & PREFIX_LOCK)
- || op != 2) {
- gen_op_ld_v(s, ot, s->T0, s->A0);
- }
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, rm);
+ tcg_gen_ld_i32(s->tmp2_i32, tcg_env,
+ offsetof(CPUX86State,
+ segs[last_seg].selector));
+ tcg_gen_st16_i32(s->tmp2_i32, tcg_env,
+ offsetof(CPUX86State, fpds));
+ tcg_gen_st_tl(last_addr, tcg_env,
+ offsetof(CPUX86State, fpdp));
}
-
- switch(op) {
- case 0: /* test */
- val = insn_get(env, s, ot);
- tcg_gen_movi_tl(s->T1, val);
- gen_op_testl_T0_T1_cc(s);
- set_cc_op(s, CC_OP_LOGICB + ot);
+ } else {
+ /* register float ops */
+ int opreg = rm;
+
+ switch (op) {
+ case 0x08: /* fld sti */
+ gen_helper_fpush(tcg_env);
+ gen_helper_fmov_ST0_STN(tcg_env,
+ tcg_constant_i32((opreg + 1) & 7));
break;
- case 2: /* not */
- if (s->prefix & PREFIX_LOCK) {
- if (mod == 3) {
- goto illegal_op;
- }
- tcg_gen_movi_tl(s->T0, ~0);
- tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0,
- s->mem_index, ot | MO_LE);
- } else {
- tcg_gen_not_tl(s->T0, s->T0);
- if (mod != 3) {
- gen_op_st_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_reg_v(s, ot, rm, s->T0);
- }
- }
+ case 0x09: /* fxchg sti */
+ case 0x29: /* fxchg4 sti, undocumented op */
+ case 0x39: /* fxchg7 sti, undocumented op */
+ gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(opreg));
break;
- case 3: /* neg */
- if (s->prefix & PREFIX_LOCK) {
- TCGLabel *label1;
- TCGv a0, t0, t1, t2;
-
- if (mod == 3) {
- goto illegal_op;
- }
- a0 = s->A0;
- t0 = s->T0;
- label1 = gen_new_label();
-
- gen_set_label(label1);
- t1 = tcg_temp_new();
- t2 = tcg_temp_new();
- tcg_gen_mov_tl(t2, t0);
- tcg_gen_neg_tl(t1, t0);
- tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1,
- s->mem_index, ot | MO_LE);
- tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1);
-
- tcg_gen_neg_tl(s->T0, t0);
- } else {
- tcg_gen_neg_tl(s->T0, s->T0);
- if (mod != 3) {
- gen_op_st_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_reg_v(s, ot, rm, s->T0);
- }
+ case 0x0a: /* grp d9/2 */
+ switch (rm) {
+ case 0: /* fnop */
+ /*
+ * check exceptions (FreeBSD FPU probe)
+ * needs to be treated as I/O because of ferr_irq
+ */
+ translator_io_start(&s->base);
+ gen_helper_fwait(tcg_env);
+ update_fip = false;
+ break;
+ default:
+ return false;
}
- gen_op_update_neg_cc(s);
- set_cc_op(s, CC_OP_SUBB + ot);
break;
- case 4: /* mul */
- switch(ot) {
- case MO_8:
- gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX);
- tcg_gen_ext8u_tl(s->T0, s->T0);
- tcg_gen_ext8u_tl(s->T1, s->T1);
- /* XXX: use 32 bit mul which could be faster */
- tcg_gen_mul_tl(s->T0, s->T0, s->T1);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00);
- set_cc_op(s, CC_OP_MULB);
+ case 0x0c: /* grp d9/4 */
+ switch (rm) {
+ case 0: /* fchs */
+ gen_helper_fchs_ST0(tcg_env);
break;
- case MO_16:
- gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX);
- tcg_gen_ext16u_tl(s->T0, s->T0);
- tcg_gen_ext16u_tl(s->T1, s->T1);
- /* XXX: use 32 bit mul which could be faster */
- tcg_gen_mul_tl(s->T0, s->T0, s->T1);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_shri_tl(s->T0, s->T0, 16);
- gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0);
- tcg_gen_mov_tl(cpu_cc_src, s->T0);
- set_cc_op(s, CC_OP_MULW);
+ case 1: /* fabs */
+ gen_helper_fabs_ST0(tcg_env);
break;
- default:
- case MO_32:
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]);
- tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32,
- s->tmp2_i32, s->tmp3_i32);
- tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32);
- tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32);
- tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
- tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]);
- set_cc_op(s, CC_OP_MULL);
+ case 4: /* ftst */
+ gen_helper_fldz_FT0(tcg_env);
+ gen_helper_fcom_ST0_FT0(tcg_env);
break;
-#ifdef TARGET_X86_64
- case MO_64:
- tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX],
- s->T0, cpu_regs[R_EAX]);
- tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
- tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]);
- set_cc_op(s, CC_OP_MULQ);
+ case 5: /* fxam */
+ gen_helper_fxam_ST0(tcg_env);
break;
-#endif
+ default:
+ return false;
}
break;
- case 5: /* imul */
- switch(ot) {
- case MO_8:
- gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX);
- tcg_gen_ext8s_tl(s->T0, s->T0);
- tcg_gen_ext8s_tl(s->T1, s->T1);
- /* XXX: use 32 bit mul which could be faster */
- tcg_gen_mul_tl(s->T0, s->T0, s->T1);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_ext8s_tl(s->tmp0, s->T0);
- tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0);
- set_cc_op(s, CC_OP_MULB);
+ case 0x0d: /* grp d9/5 */
+ {
+ switch (rm) {
+ case 0:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fld1_ST0(tcg_env);
+ break;
+ case 1:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldl2t_ST0(tcg_env);
+ break;
+ case 2:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldl2e_ST0(tcg_env);
+ break;
+ case 3:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldpi_ST0(tcg_env);
+ break;
+ case 4:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldlg2_ST0(tcg_env);
+ break;
+ case 5:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldln2_ST0(tcg_env);
+ break;
+ case 6:
+ gen_helper_fpush(tcg_env);
+ gen_helper_fldz_ST0(tcg_env);
+ break;
+ default:
+ return false;
+ }
+ }
+ break;
+ case 0x0e: /* grp d9/6 */
+ switch (rm) {
+ case 0: /* f2xm1 */
+ gen_helper_f2xm1(tcg_env);
break;
- case MO_16:
- gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX);
- tcg_gen_ext16s_tl(s->T0, s->T0);
- tcg_gen_ext16s_tl(s->T1, s->T1);
- /* XXX: use 32 bit mul which could be faster */
- tcg_gen_mul_tl(s->T0, s->T0, s->T1);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_ext16s_tl(s->tmp0, s->T0);
- tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0);
- tcg_gen_shri_tl(s->T0, s->T0, 16);
- gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0);
- set_cc_op(s, CC_OP_MULW);
+ case 1: /* fyl2x */
+ gen_helper_fyl2x(tcg_env);
break;
- default:
- case MO_32:
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]);
- tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32,
- s->tmp2_i32, s->tmp3_i32);
- tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32);
- tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32);
- tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31);
- tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
- tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32);
- tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32);
- set_cc_op(s, CC_OP_MULL);
+ case 2: /* fptan */
+ gen_helper_fptan(tcg_env);
break;
-#ifdef TARGET_X86_64
- case MO_64:
- tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX],
- s->T0, cpu_regs[R_EAX]);
- tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]);
- tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63);
- tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]);
- set_cc_op(s, CC_OP_MULQ);
+ case 3: /* fpatan */
+ gen_helper_fpatan(tcg_env);
break;
-#endif
- }
- break;
- case 6: /* div */
- switch(ot) {
- case MO_8:
- gen_helper_divb_AL(tcg_env, s->T0);
+ case 4: /* fxtract */
+ gen_helper_fxtract(tcg_env);
break;
- case MO_16:
- gen_helper_divw_AX(tcg_env, s->T0);
+ case 5: /* fprem1 */
+ gen_helper_fprem1(tcg_env);
break;
- default:
- case MO_32:
- gen_helper_divl_EAX(tcg_env, s->T0);
+ case 6: /* fdecstp */
+ gen_helper_fdecstp(tcg_env);
break;
-#ifdef TARGET_X86_64
- case MO_64:
- gen_helper_divq_EAX(tcg_env, s->T0);
+ default:
+ case 7: /* fincstp */
+ gen_helper_fincstp(tcg_env);
break;
-#endif
}
break;
- case 7: /* idiv */
- switch(ot) {
- case MO_8:
- gen_helper_idivb_AL(tcg_env, s->T0);
+ case 0x0f: /* grp d9/7 */
+ switch (rm) {
+ case 0: /* fprem */
+ gen_helper_fprem(tcg_env);
break;
- case MO_16:
- gen_helper_idivw_AX(tcg_env, s->T0);
+ case 1: /* fyl2xp1 */
+ gen_helper_fyl2xp1(tcg_env);
break;
- default:
- case MO_32:
- gen_helper_idivl_EAX(tcg_env, s->T0);
+ case 2: /* fsqrt */
+ gen_helper_fsqrt(tcg_env);
break;
-#ifdef TARGET_X86_64
- case MO_64:
- gen_helper_idivq_EAX(tcg_env, s->T0);
+ case 3: /* fsincos */
+ gen_helper_fsincos(tcg_env);
+ break;
+ case 5: /* fscale */
+ gen_helper_fscale(tcg_env);
+ break;
+ case 4: /* frndint */
+ gen_helper_frndint(tcg_env);
+ break;
+ case 6: /* fsin */
+ gen_helper_fsin(tcg_env);
+ break;
+ default:
+ case 7: /* fcos */
+ gen_helper_fcos(tcg_env);
break;
-#endif
}
break;
- default:
- goto unknown_op;
- }
- break;
-
- case 0xfe: /* GRP4 */
- case 0xff: /* GRP5 */
- ot = mo_b_d(b, dflag);
-
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
- op = (modrm >> 3) & 7;
- if (op >= 2 && b == 0xfe) {
- goto unknown_op;
- }
- if (CODE64(s)) {
- if (op == 2 || op == 4) {
- /* operand size for jumps is 64 bit */
- ot = MO_64;
- } else if (op == 3 || op == 5) {
- ot = dflag != MO_16 ? MO_32 + REX_W(s) : MO_16;
- } else if (op == 6) {
- /* default push size is 64 bit */
- ot = mo_pushpop(s, dflag);
+ case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */
+ case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */
+ case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */
+ {
+ int op1;
+
+ op1 = op & 7;
+ if (op >= 0x20) {
+ gen_helper_fp_arith_STN_ST0(op1, opreg);
+ if (op >= 0x30) {
+ gen_helper_fpop(tcg_env);
+ }
+ } else {
+ gen_helper_fmov_FT0_STN(tcg_env,
+ tcg_constant_i32(opreg));
+ gen_helper_fp_arith_ST0_FT0(op1);
+ }
}
- }
- if (mod != 3) {
- gen_lea_modrm(env, s, modrm);
- if (op >= 2 && op != 3 && op != 5)
- gen_op_ld_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, rm);
- }
-
- switch(op) {
- case 0: /* inc Ev */
- if (mod != 3)
- opreg = OR_TMP0;
- else
- opreg = rm;
- gen_inc(s, ot, opreg, 1);
break;
- case 1: /* dec Ev */
- if (mod != 3)
- opreg = OR_TMP0;
- else
- opreg = rm;
- gen_inc(s, ot, opreg, -1);
+ case 0x02: /* fcom */
+ case 0x22: /* fcom2, undocumented op */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fcom_ST0_FT0(tcg_env);
break;
- case 2: /* call Ev */
- /* XXX: optimize if memory (no 'and' is necessary) */
- if (dflag == MO_16) {
- tcg_gen_ext16u_tl(s->T0, s->T0);
- }
- gen_push_v(s, eip_next_tl(s));
- gen_op_jmp_v(s, s->T0);
- gen_bnd_jmp(s);
- s->base.is_jmp = DISAS_JUMP;
+ case 0x03: /* fcomp */
+ case 0x23: /* fcomp3, undocumented op */
+ case 0x32: /* fcomp5, undocumented op */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fcom_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
break;
- case 3: /* lcall Ev */
- if (mod == 3) {
- goto illegal_op;
- }
- gen_op_ld_v(s, ot, s->T1, s->A0);
- gen_add_A0_im(s, 1 << ot);
- gen_op_ld_v(s, MO_16, s->T0, s->A0);
- do_lcall:
- if (PE(s) && !VM86(s)) {
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- gen_helper_lcall_protected(tcg_env, s->tmp2_i32, s->T1,
- tcg_constant_i32(dflag - 1),
- eip_next_tl(s));
- } else {
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1);
- gen_helper_lcall_real(tcg_env, s->tmp2_i32, s->tmp3_i32,
- tcg_constant_i32(dflag - 1),
- eip_next_i32(s));
+ case 0x15: /* da/5 */
+ switch (rm) {
+ case 1: /* fucompp */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1));
+ gen_helper_fucom_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
+ gen_helper_fpop(tcg_env);
+ break;
+ default:
+ return false;
}
- s->base.is_jmp = DISAS_JUMP;
break;
- case 4: /* jmp Ev */
- if (dflag == MO_16) {
- tcg_gen_ext16u_tl(s->T0, s->T0);
+ case 0x1c:
+ switch (rm) {
+ case 0: /* feni (287 only, just do nop here) */
+ break;
+ case 1: /* fdisi (287 only, just do nop here) */
+ break;
+ case 2: /* fclex */
+ gen_helper_fclex(tcg_env);
+ update_fip = false;
+ break;
+ case 3: /* fninit */
+ gen_helper_fninit(tcg_env);
+ update_fip = false;
+ break;
+ case 4: /* fsetpm (287 only, just do nop here) */
+ break;
+ default:
+ return false;
}
- gen_op_jmp_v(s, s->T0);
- gen_bnd_jmp(s);
- s->base.is_jmp = DISAS_JUMP;
break;
- case 5: /* ljmp Ev */
- if (mod == 3) {
+ case 0x1d: /* fucomi */
+ if (!(s->cpuid_features & CPUID_CMOV)) {
goto illegal_op;
}
- gen_op_ld_v(s, ot, s->T1, s->A0);
- gen_add_A0_im(s, 1 << ot);
- gen_op_ld_v(s, MO_16, s->T0, s->A0);
- do_ljmp:
- if (PE(s) && !VM86(s)) {
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- gen_helper_ljmp_protected(tcg_env, s->tmp2_i32, s->T1,
- eip_next_tl(s));
- } else {
- gen_op_movl_seg_T0_vm(s, R_CS);
- gen_op_jmp_v(s, s->T1);
+ gen_update_cc_op(s);
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fucomi_ST0_FT0(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+ break;
+ case 0x1e: /* fcomi */
+ if (!(s->cpuid_features & CPUID_CMOV)) {
+ goto illegal_op;
}
- s->base.is_jmp = DISAS_JUMP;
+ gen_update_cc_op(s);
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fcomi_ST0_FT0(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
break;
- case 6: /* push Ev */
- gen_push_v(s, s->T0);
+ case 0x28: /* ffree sti */
+ gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg));
break;
- default:
- goto unknown_op;
- }
- break;
-
- case 0x84: /* test Ev, Gv */
- case 0x85:
- ot = mo_b_d(b, dflag);
-
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
-
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- gen_op_mov_v_reg(s, ot, s->T1, reg);
- gen_op_testl_T0_T1_cc(s);
- set_cc_op(s, CC_OP_LOGICB + ot);
- break;
-
- case 0xa8: /* test eAX, Iv */
- case 0xa9:
- ot = mo_b_d(b, dflag);
- val = insn_get(env, s, ot);
-
- gen_op_mov_v_reg(s, ot, s->T0, OR_EAX);
- tcg_gen_movi_tl(s->T1, val);
- gen_op_testl_T0_T1_cc(s);
- set_cc_op(s, CC_OP_LOGICB + ot);
- break;
-
- case 0x98: /* CWDE/CBW */
- switch (dflag) {
-#ifdef TARGET_X86_64
- case MO_64:
- gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX);
- tcg_gen_ext32s_tl(s->T0, s->T0);
- gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0);
+ case 0x2a: /* fst sti */
+ gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg));
break;
-#endif
- case MO_32:
- gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX);
- tcg_gen_ext16s_tl(s->T0, s->T0);
- gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0);
+ case 0x2b: /* fstp sti */
+ case 0x0b: /* fstp1 sti, undocumented op */
+ case 0x3a: /* fstp8 sti, undocumented op */
+ case 0x3b: /* fstp9 sti, undocumented op */
+ gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fpop(tcg_env);
break;
- case MO_16:
- gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX);
- tcg_gen_ext8s_tl(s->T0, s->T0);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ case 0x2c: /* fucom st(i) */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fucom_ST0_FT0(tcg_env);
break;
- default:
- g_assert_not_reached();
- }
- break;
- case 0x99: /* CDQ/CWD */
- switch (dflag) {
-#ifdef TARGET_X86_64
- case MO_64:
- gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX);
- tcg_gen_sari_tl(s->T0, s->T0, 63);
- gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0);
+ case 0x2d: /* fucomp st(i) */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fucom_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
break;
-#endif
- case MO_32:
- gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX);
- tcg_gen_ext32s_tl(s->T0, s->T0);
- tcg_gen_sari_tl(s->T0, s->T0, 31);
- gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0);
+ case 0x33: /* de/3 */
+ switch (rm) {
+ case 1: /* fcompp */
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1));
+ gen_helper_fcom_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
+ gen_helper_fpop(tcg_env);
+ break;
+ default:
+ return false;
+ }
break;
- case MO_16:
- gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX);
- tcg_gen_ext16s_tl(s->T0, s->T0);
- tcg_gen_sari_tl(s->T0, s->T0, 15);
- gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0);
+ case 0x38: /* ffreep sti, undocumented op */
+ gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fpop(tcg_env);
break;
- default:
- g_assert_not_reached();
- }
- break;
- case 0x1af: /* imul Gv, Ev */
- case 0x69: /* imul Gv, Ev, I */
- case 0x6b:
- ot = dflag;
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- if (b == 0x69)
- s->rip_offset = insn_const_size(ot);
- else if (b == 0x6b)
- s->rip_offset = 1;
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- if (b == 0x69) {
- val = insn_get(env, s, ot);
- tcg_gen_movi_tl(s->T1, val);
- } else if (b == 0x6b) {
- val = (int8_t)insn_get(env, s, MO_8);
- tcg_gen_movi_tl(s->T1, val);
- } else {
- gen_op_mov_v_reg(s, ot, s->T1, reg);
- }
- switch (ot) {
-#ifdef TARGET_X86_64
- case MO_64:
- tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1);
- 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, s->T1);
+ case 0x3c: /* df/4 */
+ switch (rm) {
+ case 0:
+ gen_helper_fnstsw(s->tmp2_i32, tcg_env);
+ tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32);
+ gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
+ break;
+ default:
+ return false;
+ }
break;
-#endif
- case MO_32:
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1);
- tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32,
- s->tmp2_i32, s->tmp3_i32);
- tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32);
- tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31);
- tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]);
- tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32);
- tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32);
+ case 0x3d: /* fucomip */
+ if (!(s->cpuid_features & CPUID_CMOV)) {
+ goto illegal_op;
+ }
+ gen_update_cc_op(s);
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fucomi_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
break;
- default:
- tcg_gen_ext16s_tl(s->T0, s->T0);
- tcg_gen_ext16s_tl(s->T1, s->T1);
- /* XXX: use 32 bit mul which could be faster */
- tcg_gen_mul_tl(s->T0, s->T0, s->T1);
- tcg_gen_mov_tl(cpu_cc_dst, s->T0);
- tcg_gen_ext16s_tl(s->tmp0, s->T0);
- tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0);
- gen_op_mov_reg_v(s, ot, reg, s->T0);
+ case 0x3e: /* fcomip */
+ if (!(s->cpuid_features & CPUID_CMOV)) {
+ goto illegal_op;
+ }
+ gen_update_cc_op(s);
+ gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
+ gen_helper_fcomi_ST0_FT0(tcg_env);
+ gen_helper_fpop(tcg_env);
+ set_cc_op(s, CC_OP_EFLAGS);
+ break;
+ case 0x10 ... 0x13: /* fcmovxx */
+ case 0x18 ... 0x1b:
+ {
+ int op1;
+ TCGLabel *l1;
+ static const uint8_t fcmov_cc[8] = {
+ (JCC_B << 1),
+ (JCC_Z << 1),
+ (JCC_BE << 1),
+ (JCC_P << 1),
+ };
+
+ if (!(s->cpuid_features & CPUID_CMOV)) {
+ goto illegal_op;
+ }
+ op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1);
+ l1 = gen_new_label();
+ gen_jcc1_noeob(s, op1, l1);
+ gen_helper_fmov_ST0_STN(tcg_env,
+ tcg_constant_i32(opreg));
+ gen_set_label(l1);
+ }
break;
+ default:
+ return false;
}
- set_cc_op(s, CC_OP_MULB + ot);
- break;
+ }
+
+ if (update_fip) {
+ tcg_gen_ld_i32(s->tmp2_i32, tcg_env,
+ offsetof(CPUX86State, segs[R_CS].selector));
+ tcg_gen_st16_i32(s->tmp2_i32, tcg_env,
+ offsetof(CPUX86State, fpcs));
+ tcg_gen_st_tl(eip_cur_tl(s),
+ tcg_env, offsetof(CPUX86State, fpip));
+ }
+ return true;
+
+ illegal_op:
+ gen_illegal_opcode(s);
+ return true;
+}
+
+static void disas_insn_old(DisasContext *s, CPUState *cpu, int b)
+{
+ CPUX86State *env = cpu_env(cpu);
+ int prefixes = s->prefix;
+ MemOp dflag = s->dflag;
+ int shift;
+ MemOp ot;
+ int modrm, reg, rm, mod, op, opreg, val;
+
+ /* now check op code */
+ switch (b) {
+ /**************************/
+ /* arith & logic */
case 0x1c0:
case 0x1c1: /* xadd Ev, Gv */
ot = mo_b_d(b, dflag);
@@ -3970,375 +3270,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
break;
/**************************/
- /* push/pop */
- case 0x50 ... 0x57: /* push */
- gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s));
- gen_push_v(s, s->T0);
- break;
- case 0x58 ... 0x5f: /* pop */
- ot = gen_pop_T0(s);
- /* NOTE: order is important for pop %sp */
- gen_pop_update(s, ot);
- gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0);
- break;
- case 0x60: /* pusha */
- if (CODE64(s))
- goto illegal_op;
- gen_pusha(s);
- break;
- case 0x61: /* popa */
- if (CODE64(s))
- goto illegal_op;
- gen_popa(s);
- break;
- case 0x68: /* push Iv */
- case 0x6a:
- ot = mo_pushpop(s, dflag);
- if (b == 0x68)
- val = insn_get(env, s, ot);
- else
- val = (int8_t)insn_get(env, s, MO_8);
- tcg_gen_movi_tl(s->T0, val);
- gen_push_v(s, s->T0);
- break;
- case 0x8f: /* pop Ev */
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- ot = gen_pop_T0(s);
- if (mod == 3) {
- /* NOTE: order is important for pop %sp */
- gen_pop_update(s, ot);
- rm = (modrm & 7) | REX_B(s);
- gen_op_mov_reg_v(s, ot, rm, s->T0);
- } 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, ot);
- }
- break;
- case 0xc8: /* enter */
- {
- int level;
- val = x86_lduw_code(env, s);
- level = x86_ldub_code(env, s);
- gen_enter(s, val, level);
- }
- break;
- case 0xc9: /* leave */
- gen_leave(s);
- break;
- case 0x06: /* push es */
- case 0x0e: /* push cs */
- case 0x16: /* push ss */
- case 0x1e: /* push ds */
- if (CODE64(s))
- goto illegal_op;
- gen_op_movl_T0_seg(s, b >> 3);
- gen_push_v(s, s->T0);
- break;
- case 0x1a0: /* push fs */
- case 0x1a8: /* push gs */
- gen_op_movl_T0_seg(s, (b >> 3) & 7);
- gen_push_v(s, s->T0);
- break;
- case 0x07: /* pop es */
- case 0x17: /* pop ss */
- case 0x1f: /* pop ds */
- if (CODE64(s))
- goto illegal_op;
- reg = b >> 3;
- ot = gen_pop_T0(s);
- gen_movl_seg_T0(s, reg);
- gen_pop_update(s, ot);
- break;
- case 0x1a1: /* pop fs */
- case 0x1a9: /* pop gs */
- ot = gen_pop_T0(s);
- gen_movl_seg_T0(s, (b >> 3) & 7);
- gen_pop_update(s, ot);
- break;
-
- /**************************/
- /* mov */
- case 0x88:
- case 0x89: /* mov Gv, Ev */
- ot = mo_b_d(b, dflag);
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
-
- /* generate a generic store */
- gen_ldst_modrm(env, s, modrm, ot, reg, 1);
- break;
- case 0xc6:
- case 0xc7: /* mov Ev, Iv */
- ot = mo_b_d(b, dflag);
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- if (mod != 3) {
- s->rip_offset = insn_const_size(ot);
- gen_lea_modrm(env, s, modrm);
- }
- val = insn_get(env, s, ot);
- tcg_gen_movi_tl(s->T0, val);
- if (mod != 3) {
- gen_op_st_v(s, ot, s->T0, s->A0);
- } else {
- gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0);
- }
- break;
- case 0x8a:
- case 0x8b: /* mov Ev, Gv */
- ot = mo_b_d(b, dflag);
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
-
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- gen_op_mov_reg_v(s, ot, reg, s->T0);
- break;
- case 0x8e: /* mov seg, Gv */
- modrm = x86_ldub_code(env, s);
- reg = (modrm >> 3) & 7;
- if (reg >= 6 || reg == R_CS)
- goto illegal_op;
- gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0);
- gen_movl_seg_T0(s, reg);
- break;
- case 0x8c: /* mov Gv, seg */
- modrm = x86_ldub_code(env, s);
- reg = (modrm >> 3) & 7;
- mod = (modrm >> 6) & 3;
- if (reg >= 6)
- goto illegal_op;
- gen_op_movl_T0_seg(s, reg);
- ot = mod == 3 ? dflag : MO_16;
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
- break;
-
- case 0x1b6: /* movzbS Gv, Eb */
- case 0x1b7: /* movzwS Gv, Eb */
- case 0x1be: /* movsbS Gv, Eb */
- case 0x1bf: /* movswS Gv, Eb */
- {
- MemOp d_ot;
- MemOp s_ot;
-
- /* d_ot is the size of destination */
- d_ot = dflag;
- /* ot is the size of source */
- ot = (b & 1) + MO_8;
- /* s_ot is the sign+size of source */
- s_ot = b & 8 ? MO_SIGN | ot : ot;
-
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
-
- if (mod == 3) {
- if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) {
- tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, rm);
- switch (s_ot) {
- case MO_UB:
- tcg_gen_ext8u_tl(s->T0, s->T0);
- break;
- case MO_SB:
- tcg_gen_ext8s_tl(s->T0, s->T0);
- break;
- case MO_UW:
- tcg_gen_ext16u_tl(s->T0, s->T0);
- break;
- default:
- case MO_SW:
- tcg_gen_ext16s_tl(s->T0, s->T0);
- break;
- }
- }
- gen_op_mov_reg_v(s, d_ot, reg, s->T0);
- } else {
- gen_lea_modrm(env, s, modrm);
- gen_op_ld_v(s, s_ot, s->T0, s->A0);
- gen_op_mov_reg_v(s, d_ot, reg, s->T0);
- }
- }
- break;
-
- case 0x8d: /* lea */
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- if (mod == 3)
- goto illegal_op;
- reg = ((modrm >> 3) & 7) | REX_R(s);
- {
- AddressParts a = gen_lea_modrm_0(env, s, modrm);
- TCGv ea = gen_lea_modrm_1(s, a, false);
- gen_lea_v_seg(s, s->aflag, ea, -1, -1);
- gen_op_mov_reg_v(s, dflag, reg, s->A0);
- }
- break;
-
- case 0xa0: /* mov EAX, Ov */
- case 0xa1:
- case 0xa2: /* mov Ov, EAX */
- case 0xa3:
- {
- target_ulong offset_addr;
-
- ot = mo_b_d(b, dflag);
- offset_addr = insn_get_addr(env, s, s->aflag);
- tcg_gen_movi_tl(s->A0, offset_addr);
- gen_add_A0_ds_seg(s);
- if ((b & 2) == 0) {
- gen_op_ld_v(s, ot, s->T0, s->A0);
- gen_op_mov_reg_v(s, ot, R_EAX, s->T0);
- } else {
- gen_op_mov_v_reg(s, ot, s->T0, R_EAX);
- gen_op_st_v(s, ot, s->T0, s->A0);
- }
- }
- break;
- case 0xd7: /* xlat */
- tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]);
- tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]);
- tcg_gen_add_tl(s->A0, s->A0, s->T0);
- gen_add_A0_ds_seg(s);
- gen_op_ld_v(s, MO_8, s->T0, s->A0);
- gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0);
- break;
- case 0xb0 ... 0xb7: /* mov R, Ib */
- val = insn_get(env, s, MO_8);
- tcg_gen_movi_tl(s->T0, val);
- gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0);
- break;
- case 0xb8 ... 0xbf: /* mov R, Iv */
-#ifdef TARGET_X86_64
- if (dflag == MO_64) {
- uint64_t tmp;
- /* 64 bit case */
- tmp = x86_ldq_code(env, s);
- reg = (b & 7) | REX_B(s);
- tcg_gen_movi_tl(s->T0, tmp);
- gen_op_mov_reg_v(s, MO_64, reg, s->T0);
- } else
-#endif
- {
- ot = dflag;
- val = insn_get(env, s, ot);
- reg = (b & 7) | REX_B(s);
- tcg_gen_movi_tl(s->T0, val);
- gen_op_mov_reg_v(s, ot, reg, s->T0);
- }
- break;
-
- case 0x91 ... 0x97: /* xchg R, EAX */
- do_xchg_reg_eax:
- ot = dflag;
- reg = (b & 7) | REX_B(s);
- rm = R_EAX;
- goto do_xchg_reg;
- case 0x86:
- case 0x87: /* xchg Ev, Gv */
- ot = mo_b_d(b, dflag);
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- mod = (modrm >> 6) & 3;
- if (mod == 3) {
- rm = (modrm & 7) | REX_B(s);
- do_xchg_reg:
- gen_op_mov_v_reg(s, ot, s->T0, reg);
- gen_op_mov_v_reg(s, ot, s->T1, rm);
- gen_op_mov_reg_v(s, ot, rm, s->T0);
- gen_op_mov_reg_v(s, ot, reg, s->T1);
- } else {
- gen_lea_modrm(env, s, modrm);
- gen_op_mov_v_reg(s, ot, s->T0, reg);
- /* for xchg, lock is implicit */
- tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0,
- s->mem_index, ot | MO_LE);
- gen_op_mov_reg_v(s, ot, reg, s->T1);
- }
- break;
- case 0xc4: /* les Gv */
- /* In CODE64 this is VEX3; see above. */
- op = R_ES;
- goto do_lxx;
- case 0xc5: /* lds Gv */
- /* In CODE64 this is VEX2; see above. */
- op = R_DS;
- goto do_lxx;
- case 0x1b2: /* lss Gv */
- op = R_SS;
- goto do_lxx;
- case 0x1b4: /* lfs Gv */
- op = R_FS;
- goto do_lxx;
- case 0x1b5: /* lgs Gv */
- op = R_GS;
- do_lxx:
- ot = dflag != MO_16 ? MO_32 : MO_16;
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- mod = (modrm >> 6) & 3;
- if (mod == 3)
- goto illegal_op;
- gen_lea_modrm(env, s, modrm);
- gen_op_ld_v(s, ot, s->T1, s->A0);
- gen_add_A0_im(s, 1 << ot);
- /* load the segment first to handle exceptions properly */
- gen_op_ld_v(s, MO_16, s->T0, s->A0);
- gen_movl_seg_T0(s, op);
- /* then put the data */
- gen_op_mov_reg_v(s, ot, reg, s->T1);
- break;
-
- /************************/
/* shifts */
- case 0xc0:
- case 0xc1:
- /* shift Ev,Ib */
- shift = 2;
- grp2:
- {
- ot = mo_b_d(b, dflag);
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- op = (modrm >> 3) & 7;
-
- if (mod != 3) {
- if (shift == 2) {
- s->rip_offset = 1;
- }
- gen_lea_modrm(env, s, modrm);
- opreg = OR_TMP0;
- } else {
- opreg = (modrm & 7) | REX_B(s);
- }
-
- /* simpler op */
- if (shift == 0) {
- gen_shift(s, op, ot, opreg, OR_ECX);
- } else {
- if (shift == 2) {
- shift = x86_ldub_code(env, s);
- }
- gen_shifti(s, op, ot, opreg, shift);
- }
- }
- break;
- case 0xd0:
- case 0xd1:
- /* shift Ev,1 */
- shift = 1;
- goto grp2;
- case 0xd2:
- case 0xd3:
- /* shift Ev,cl */
- shift = 0;
- goto grp2;
-
case 0x1a4: /* shld imm */
op = 0;
shift = 1;
@@ -4377,929 +3309,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
break;
/************************/
- /* floats */
- case 0xd8 ... 0xdf:
- {
- bool update_fip = true;
-
- if (s->flags & (HF_EM_MASK | HF_TS_MASK)) {
- /* if CR0.EM or CR0.TS are set, generate an FPU exception */
- /* XXX: what to do if illegal op ? */
- gen_exception(s, EXCP07_PREX);
- break;
- }
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- rm = modrm & 7;
- op = ((b & 7) << 3) | ((modrm >> 3) & 7);
- if (mod != 3) {
- /* memory op */
- AddressParts a = gen_lea_modrm_0(env, s, modrm);
- TCGv ea = gen_lea_modrm_1(s, a, false);
- TCGv last_addr = tcg_temp_new();
- bool update_fdp = true;
-
- tcg_gen_mov_tl(last_addr, ea);
- gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override);
-
- switch (op) {
- case 0x00 ... 0x07: /* fxxxs */
- case 0x10 ... 0x17: /* fixxxl */
- case 0x20 ... 0x27: /* fxxxl */
- case 0x30 ... 0x37: /* fixxx */
- {
- int op1;
- op1 = op & 7;
-
- switch (op >> 4) {
- case 0:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- gen_helper_flds_FT0(tcg_env, s->tmp2_i32);
- break;
- case 1:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- gen_helper_fildl_FT0(tcg_env, s->tmp2_i32);
- break;
- case 2:
- tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- gen_helper_fldl_FT0(tcg_env, s->tmp1_i64);
- break;
- case 3:
- default:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LESW);
- gen_helper_fildl_FT0(tcg_env, s->tmp2_i32);
- break;
- }
-
- gen_helper_fp_arith_ST0_FT0(op1);
- if (op1 == 3) {
- /* fcomp needs pop */
- gen_helper_fpop(tcg_env);
- }
- }
- break;
- case 0x08: /* flds */
- case 0x0a: /* fsts */
- case 0x0b: /* fstps */
- case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */
- case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */
- case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */
- switch (op & 7) {
- case 0:
- switch (op >> 4) {
- case 0:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- gen_helper_flds_ST0(tcg_env, s->tmp2_i32);
- break;
- case 1:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- gen_helper_fildl_ST0(tcg_env, s->tmp2_i32);
- break;
- case 2:
- tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- gen_helper_fldl_ST0(tcg_env, s->tmp1_i64);
- break;
- case 3:
- default:
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LESW);
- gen_helper_fildl_ST0(tcg_env, s->tmp2_i32);
- break;
- }
- break;
- case 1:
- /* XXX: the corresponding CPUID bit must be tested ! */
- switch (op >> 4) {
- case 1:
- gen_helper_fisttl_ST0(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- break;
- case 2:
- gen_helper_fisttll_ST0(s->tmp1_i64, tcg_env);
- tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- break;
- case 3:
- default:
- gen_helper_fistt_ST0(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUW);
- break;
- }
- gen_helper_fpop(tcg_env);
- break;
- default:
- switch (op >> 4) {
- case 0:
- gen_helper_fsts_ST0(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- break;
- case 1:
- gen_helper_fistl_ST0(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUL);
- break;
- case 2:
- gen_helper_fstl_ST0(s->tmp1_i64, tcg_env);
- tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- break;
- case 3:
- default:
- gen_helper_fist_ST0(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUW);
- break;
- }
- if ((op & 7) == 3) {
- gen_helper_fpop(tcg_env);
- }
- break;
- }
- break;
- case 0x0c: /* fldenv mem */
- gen_helper_fldenv(tcg_env, s->A0,
- tcg_constant_i32(dflag - 1));
- update_fip = update_fdp = false;
- break;
- case 0x0d: /* fldcw mem */
- tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUW);
- gen_helper_fldcw(tcg_env, s->tmp2_i32);
- update_fip = update_fdp = false;
- break;
- case 0x0e: /* fnstenv mem */
- gen_helper_fstenv(tcg_env, s->A0,
- tcg_constant_i32(dflag - 1));
- update_fip = update_fdp = false;
- break;
- case 0x0f: /* fnstcw mem */
- gen_helper_fnstcw(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUW);
- update_fip = update_fdp = false;
- break;
- case 0x1d: /* fldt mem */
- gen_helper_fldt_ST0(tcg_env, s->A0);
- break;
- case 0x1f: /* fstpt mem */
- gen_helper_fstt_ST0(tcg_env, s->A0);
- gen_helper_fpop(tcg_env);
- break;
- case 0x2c: /* frstor mem */
- gen_helper_frstor(tcg_env, s->A0,
- tcg_constant_i32(dflag - 1));
- update_fip = update_fdp = false;
- break;
- case 0x2e: /* fnsave mem */
- gen_helper_fsave(tcg_env, s->A0,
- tcg_constant_i32(dflag - 1));
- update_fip = update_fdp = false;
- break;
- case 0x2f: /* fnstsw mem */
- gen_helper_fnstsw(s->tmp2_i32, tcg_env);
- tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0,
- s->mem_index, MO_LEUW);
- update_fip = update_fdp = false;
- break;
- case 0x3c: /* fbld */
- gen_helper_fbld_ST0(tcg_env, s->A0);
- break;
- case 0x3e: /* fbstp */
- gen_helper_fbst_ST0(tcg_env, s->A0);
- gen_helper_fpop(tcg_env);
- break;
- case 0x3d: /* fildll */
- tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- gen_helper_fildll_ST0(tcg_env, s->tmp1_i64);
- break;
- case 0x3f: /* fistpll */
- gen_helper_fistll_ST0(s->tmp1_i64, tcg_env);
- tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0,
- s->mem_index, MO_LEUQ);
- gen_helper_fpop(tcg_env);
- break;
- default:
- goto unknown_op;
- }
-
- if (update_fdp) {
- int last_seg = s->override >= 0 ? s->override : a.def_seg;
-
- tcg_gen_ld_i32(s->tmp2_i32, tcg_env,
- offsetof(CPUX86State,
- segs[last_seg].selector));
- tcg_gen_st16_i32(s->tmp2_i32, tcg_env,
- offsetof(CPUX86State, fpds));
- tcg_gen_st_tl(last_addr, tcg_env,
- offsetof(CPUX86State, fpdp));
- }
- } else {
- /* register float ops */
- opreg = rm;
-
- switch (op) {
- case 0x08: /* fld sti */
- gen_helper_fpush(tcg_env);
- gen_helper_fmov_ST0_STN(tcg_env,
- tcg_constant_i32((opreg + 1) & 7));
- break;
- case 0x09: /* fxchg sti */
- case 0x29: /* fxchg4 sti, undocumented op */
- case 0x39: /* fxchg7 sti, undocumented op */
- gen_helper_fxchg_ST0_STN(tcg_env, tcg_constant_i32(opreg));
- break;
- case 0x0a: /* grp d9/2 */
- switch (rm) {
- case 0: /* fnop */
- /*
- * check exceptions (FreeBSD FPU probe)
- * needs to be treated as I/O because of ferr_irq
- */
- translator_io_start(&s->base);
- gen_helper_fwait(tcg_env);
- update_fip = false;
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x0c: /* grp d9/4 */
- switch (rm) {
- case 0: /* fchs */
- gen_helper_fchs_ST0(tcg_env);
- break;
- case 1: /* fabs */
- gen_helper_fabs_ST0(tcg_env);
- break;
- case 4: /* ftst */
- gen_helper_fldz_FT0(tcg_env);
- gen_helper_fcom_ST0_FT0(tcg_env);
- break;
- case 5: /* fxam */
- gen_helper_fxam_ST0(tcg_env);
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x0d: /* grp d9/5 */
- {
- switch (rm) {
- case 0:
- gen_helper_fpush(tcg_env);
- gen_helper_fld1_ST0(tcg_env);
- break;
- case 1:
- gen_helper_fpush(tcg_env);
- gen_helper_fldl2t_ST0(tcg_env);
- break;
- case 2:
- gen_helper_fpush(tcg_env);
- gen_helper_fldl2e_ST0(tcg_env);
- break;
- case 3:
- gen_helper_fpush(tcg_env);
- gen_helper_fldpi_ST0(tcg_env);
- break;
- case 4:
- gen_helper_fpush(tcg_env);
- gen_helper_fldlg2_ST0(tcg_env);
- break;
- case 5:
- gen_helper_fpush(tcg_env);
- gen_helper_fldln2_ST0(tcg_env);
- break;
- case 6:
- gen_helper_fpush(tcg_env);
- gen_helper_fldz_ST0(tcg_env);
- break;
- default:
- goto unknown_op;
- }
- }
- break;
- case 0x0e: /* grp d9/6 */
- switch (rm) {
- case 0: /* f2xm1 */
- gen_helper_f2xm1(tcg_env);
- break;
- case 1: /* fyl2x */
- gen_helper_fyl2x(tcg_env);
- break;
- case 2: /* fptan */
- gen_helper_fptan(tcg_env);
- break;
- case 3: /* fpatan */
- gen_helper_fpatan(tcg_env);
- break;
- case 4: /* fxtract */
- gen_helper_fxtract(tcg_env);
- break;
- case 5: /* fprem1 */
- gen_helper_fprem1(tcg_env);
- break;
- case 6: /* fdecstp */
- gen_helper_fdecstp(tcg_env);
- break;
- default:
- case 7: /* fincstp */
- gen_helper_fincstp(tcg_env);
- break;
- }
- break;
- case 0x0f: /* grp d9/7 */
- switch (rm) {
- case 0: /* fprem */
- gen_helper_fprem(tcg_env);
- break;
- case 1: /* fyl2xp1 */
- gen_helper_fyl2xp1(tcg_env);
- break;
- case 2: /* fsqrt */
- gen_helper_fsqrt(tcg_env);
- break;
- case 3: /* fsincos */
- gen_helper_fsincos(tcg_env);
- break;
- case 5: /* fscale */
- gen_helper_fscale(tcg_env);
- break;
- case 4: /* frndint */
- gen_helper_frndint(tcg_env);
- break;
- case 6: /* fsin */
- gen_helper_fsin(tcg_env);
- break;
- default:
- case 7: /* fcos */
- gen_helper_fcos(tcg_env);
- break;
- }
- break;
- case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */
- case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */
- case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */
- {
- int op1;
-
- op1 = op & 7;
- if (op >= 0x20) {
- gen_helper_fp_arith_STN_ST0(op1, opreg);
- if (op >= 0x30) {
- gen_helper_fpop(tcg_env);
- }
- } else {
- gen_helper_fmov_FT0_STN(tcg_env,
- tcg_constant_i32(opreg));
- gen_helper_fp_arith_ST0_FT0(op1);
- }
- }
- break;
- case 0x02: /* fcom */
- case 0x22: /* fcom2, undocumented op */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fcom_ST0_FT0(tcg_env);
- break;
- case 0x03: /* fcomp */
- case 0x23: /* fcomp3, undocumented op */
- case 0x32: /* fcomp5, undocumented op */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fcom_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- break;
- case 0x15: /* da/5 */
- switch (rm) {
- case 1: /* fucompp */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1));
- gen_helper_fucom_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- gen_helper_fpop(tcg_env);
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x1c:
- switch (rm) {
- case 0: /* feni (287 only, just do nop here) */
- break;
- case 1: /* fdisi (287 only, just do nop here) */
- break;
- case 2: /* fclex */
- gen_helper_fclex(tcg_env);
- update_fip = false;
- break;
- case 3: /* fninit */
- gen_helper_fninit(tcg_env);
- update_fip = false;
- break;
- case 4: /* fsetpm (287 only, just do nop here) */
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x1d: /* fucomi */
- if (!(s->cpuid_features & CPUID_CMOV)) {
- goto illegal_op;
- }
- gen_update_cc_op(s);
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fucomi_ST0_FT0(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x1e: /* fcomi */
- if (!(s->cpuid_features & CPUID_CMOV)) {
- goto illegal_op;
- }
- gen_update_cc_op(s);
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fcomi_ST0_FT0(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x28: /* ffree sti */
- gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg));
- break;
- case 0x2a: /* fst sti */
- gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg));
- break;
- case 0x2b: /* fstp sti */
- case 0x0b: /* fstp1 sti, undocumented op */
- case 0x3a: /* fstp8 sti, undocumented op */
- case 0x3b: /* fstp9 sti, undocumented op */
- gen_helper_fmov_STN_ST0(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fpop(tcg_env);
- break;
- case 0x2c: /* fucom st(i) */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fucom_ST0_FT0(tcg_env);
- break;
- case 0x2d: /* fucomp st(i) */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fucom_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- break;
- case 0x33: /* de/3 */
- switch (rm) {
- case 1: /* fcompp */
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(1));
- gen_helper_fcom_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- gen_helper_fpop(tcg_env);
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x38: /* ffreep sti, undocumented op */
- gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fpop(tcg_env);
- break;
- case 0x3c: /* df/4 */
- switch (rm) {
- case 0:
- gen_helper_fnstsw(s->tmp2_i32, tcg_env);
- tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32);
- gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0);
- break;
- default:
- goto unknown_op;
- }
- break;
- case 0x3d: /* fucomip */
- if (!(s->cpuid_features & CPUID_CMOV)) {
- goto illegal_op;
- }
- gen_update_cc_op(s);
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fucomi_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x3e: /* fcomip */
- if (!(s->cpuid_features & CPUID_CMOV)) {
- goto illegal_op;
- }
- gen_update_cc_op(s);
- gen_helper_fmov_FT0_STN(tcg_env, tcg_constant_i32(opreg));
- gen_helper_fcomi_ST0_FT0(tcg_env);
- gen_helper_fpop(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x10 ... 0x13: /* fcmovxx */
- case 0x18 ... 0x1b:
- {
- int op1;
- TCGLabel *l1;
- static const uint8_t fcmov_cc[8] = {
- (JCC_B << 1),
- (JCC_Z << 1),
- (JCC_BE << 1),
- (JCC_P << 1),
- };
-
- if (!(s->cpuid_features & CPUID_CMOV)) {
- goto illegal_op;
- }
- op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1);
- l1 = gen_new_label();
- gen_jcc1_noeob(s, op1, l1);
- gen_helper_fmov_ST0_STN(tcg_env,
- tcg_constant_i32(opreg));
- gen_set_label(l1);
- }
- break;
- default:
- goto unknown_op;
- }
- }
-
- if (update_fip) {
- tcg_gen_ld_i32(s->tmp2_i32, tcg_env,
- offsetof(CPUX86State, segs[R_CS].selector));
- tcg_gen_st16_i32(s->tmp2_i32, tcg_env,
- offsetof(CPUX86State, fpcs));
- tcg_gen_st_tl(eip_cur_tl(s),
- tcg_env, offsetof(CPUX86State, fpip));
- }
- }
- break;
- /************************/
- /* string ops */
-
- case 0xa4: /* movsS */
- case 0xa5:
- ot = mo_b_d(b, dflag);
- if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
- gen_repz_movs(s, ot);
- } else {
- gen_movs(s, ot);
- }
- break;
-
- case 0xaa: /* stosS */
- case 0xab:
- ot = mo_b_d(b, dflag);
- gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX);
- if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
- gen_repz_stos(s, ot);
- } else {
- gen_stos(s, ot);
- }
- break;
- case 0xac: /* lodsS */
- case 0xad:
- ot = mo_b_d(b, dflag);
- if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
- gen_repz_lods(s, ot);
- } else {
- gen_lods(s, ot);
- }
- break;
- case 0xae: /* scasS */
- case 0xaf:
- ot = mo_b_d(b, dflag);
- gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX);
- if (prefixes & PREFIX_REPNZ) {
- gen_repz_scas(s, ot, 1);
- } else if (prefixes & PREFIX_REPZ) {
- gen_repz_scas(s, ot, 0);
- } else {
- gen_scas(s, ot);
- }
- break;
-
- case 0xa6: /* cmpsS */
- case 0xa7:
- ot = mo_b_d(b, dflag);
- if (prefixes & PREFIX_REPNZ) {
- gen_repz_cmps(s, ot, 1);
- } else if (prefixes & PREFIX_REPZ) {
- gen_repz_cmps(s, ot, 0);
- } else {
- gen_cmps(s, ot);
- }
- break;
- case 0x6c: /* insS */
- case 0x6d:
- ot = mo_b_d32(b, dflag);
- tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]);
- tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32);
- if (!gen_check_io(s, ot, s->tmp2_i32,
- SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) {
- break;
- }
- translator_io_start(&s->base);
- if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
- gen_repz_ins(s, ot);
- } else {
- gen_ins(s, ot);
- }
- break;
- case 0x6e: /* outsS */
- case 0x6f:
- ot = mo_b_d32(b, dflag);
- tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]);
- tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32);
- if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_STR_MASK)) {
- break;
- }
- translator_io_start(&s->base);
- if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
- gen_repz_outs(s, ot);
- } else {
- gen_outs(s, ot);
- }
- break;
-
- /************************/
- /* port I/O */
-
- case 0xe4:
- case 0xe5:
- ot = mo_b_d32(b, dflag);
- val = x86_ldub_code(env, s);
- tcg_gen_movi_i32(s->tmp2_i32, val);
- if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) {
- break;
- }
- translator_io_start(&s->base);
- gen_helper_in_func(ot, s->T1, s->tmp2_i32);
- gen_op_mov_reg_v(s, ot, R_EAX, s->T1);
- gen_bpt_io(s, s->tmp2_i32, ot);
- break;
- case 0xe6:
- case 0xe7:
- ot = mo_b_d32(b, dflag);
- val = x86_ldub_code(env, s);
- tcg_gen_movi_i32(s->tmp2_i32, val);
- if (!gen_check_io(s, ot, s->tmp2_i32, 0)) {
- break;
- }
- translator_io_start(&s->base);
- gen_op_mov_v_reg(s, ot, s->T1, R_EAX);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1);
- gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32);
- gen_bpt_io(s, s->tmp2_i32, ot);
- break;
- case 0xec:
- case 0xed:
- ot = mo_b_d32(b, dflag);
- tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]);
- tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32);
- if (!gen_check_io(s, ot, s->tmp2_i32, SVM_IOIO_TYPE_MASK)) {
- break;
- }
- translator_io_start(&s->base);
- gen_helper_in_func(ot, s->T1, s->tmp2_i32);
- gen_op_mov_reg_v(s, ot, R_EAX, s->T1);
- gen_bpt_io(s, s->tmp2_i32, ot);
- break;
- case 0xee:
- case 0xef:
- ot = mo_b_d32(b, dflag);
- tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]);
- tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32);
- if (!gen_check_io(s, ot, s->tmp2_i32, 0)) {
- break;
- }
- translator_io_start(&s->base);
- gen_op_mov_v_reg(s, ot, s->T1, R_EAX);
- tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1);
- gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32);
- gen_bpt_io(s, s->tmp2_i32, ot);
- break;
-
- /************************/
- /* control */
- case 0xc2: /* ret im */
- val = x86_ldsw_code(env, s);
- 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(s, s->T0);
- gen_bnd_jmp(s);
- s->base.is_jmp = DISAS_JUMP;
- break;
- case 0xc3: /* ret */
- ot = gen_pop_T0(s);
- gen_pop_update(s, ot);
- /* Note that gen_pop_T0 uses a zero-extending load. */
- gen_op_jmp_v(s, s->T0);
- gen_bnd_jmp(s);
- s->base.is_jmp = DISAS_JUMP;
- break;
- case 0xca: /* lret im */
- val = x86_ldsw_code(env, s);
- do_lret:
- if (PE(s) && !VM86(s)) {
- gen_update_cc_op(s);
- gen_update_eip_cur(s);
- gen_helper_lret_protected(tcg_env, tcg_constant_i32(dflag - 1),
- tcg_constant_i32(val));
- } else {
- gen_stack_A0(s);
- /* pop offset */
- gen_op_ld_v(s, dflag, s->T0, s->A0);
- /* NOTE: keeping EIP updated is not a problem in case of
- exception */
- gen_op_jmp_v(s, s->T0);
- /* pop selector */
- gen_add_A0_im(s, 1 << dflag);
- gen_op_ld_v(s, dflag, s->T0, s->A0);
- gen_op_movl_seg_T0_vm(s, R_CS);
- /* add stack offset */
- gen_stack_update(s, val + (2 << dflag));
- }
- s->base.is_jmp = DISAS_EOB_ONLY;
- break;
- case 0xcb: /* lret */
- val = 0;
- goto do_lret;
- case 0xcf: /* iret */
- gen_svm_check_intercept(s, SVM_EXIT_IRET);
- if (!PE(s) || VM86(s)) {
- /* real mode or vm86 mode */
- if (!check_vm86_iopl(s)) {
- break;
- }
- gen_helper_iret_real(tcg_env, tcg_constant_i32(dflag - 1));
- } else {
- gen_helper_iret_protected(tcg_env, tcg_constant_i32(dflag - 1),
- eip_next_i32(s));
- }
- set_cc_op(s, CC_OP_EFLAGS);
- s->base.is_jmp = DISAS_EOB_ONLY;
- break;
- case 0xe8: /* call im */
- {
- int diff = (dflag != MO_16
- ? (int32_t)insn_get(env, s, MO_32)
- : (int16_t)insn_get(env, s, MO_16));
- gen_push_v(s, eip_next_tl(s));
- gen_bnd_jmp(s);
- gen_jmp_rel(s, dflag, diff, 0);
- }
- break;
- case 0x9a: /* lcall im */
- {
- unsigned int selector, offset;
-
- if (CODE64(s))
- goto illegal_op;
- ot = dflag;
- offset = insn_get(env, s, ot);
- selector = insn_get(env, s, MO_16);
-
- tcg_gen_movi_tl(s->T0, selector);
- tcg_gen_movi_tl(s->T1, offset);
- }
- goto do_lcall;
- case 0xe9: /* jmp im */
- {
- int diff = (dflag != MO_16
- ? (int32_t)insn_get(env, s, MO_32)
- : (int16_t)insn_get(env, s, MO_16));
- gen_bnd_jmp(s);
- gen_jmp_rel(s, dflag, diff, 0);
- }
- break;
- case 0xea: /* ljmp im */
- {
- unsigned int selector, offset;
-
- if (CODE64(s))
- goto illegal_op;
- ot = dflag;
- offset = insn_get(env, s, ot);
- selector = insn_get(env, s, MO_16);
-
- tcg_gen_movi_tl(s->T0, selector);
- tcg_gen_movi_tl(s->T1, offset);
- }
- goto do_ljmp;
- case 0xeb: /* jmp Jb */
- {
- int diff = (int8_t)insn_get(env, s, MO_8);
- gen_jmp_rel(s, dflag, diff, 0);
- }
- break;
- case 0x70 ... 0x7f: /* jcc Jb */
- {
- int diff = (int8_t)insn_get(env, s, MO_8);
- gen_bnd_jmp(s);
- gen_jcc(s, b, diff);
- }
- break;
- case 0x180 ... 0x18f: /* jcc Jv */
- {
- int diff = (dflag != MO_16
- ? (int32_t)insn_get(env, s, MO_32)
- : (int16_t)insn_get(env, s, MO_16));
- gen_bnd_jmp(s);
- gen_jcc(s, b, diff);
- }
- break;
-
- case 0x190 ... 0x19f: /* setcc Gv */
- modrm = x86_ldub_code(env, s);
- gen_setcc1(s, b, s->T0);
- 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;
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
- gen_cmovcc1(s, b ^ 1, s->T0, cpu_regs[reg]);
- gen_op_mov_reg_v(s, ot, reg, s->T0);
- break;
-
- /************************/
- /* flags */
- case 0x9c: /* pushf */
- gen_svm_check_intercept(s, SVM_EXIT_PUSHF);
- if (check_vm86_iopl(s)) {
- gen_update_cc_op(s);
- gen_helper_read_eflags(s->T0, tcg_env);
- gen_push_v(s, s->T0);
- }
- break;
- case 0x9d: /* popf */
- gen_svm_check_intercept(s, SVM_EXIT_POPF);
- if (check_vm86_iopl(s)) {
- int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK;
-
- if (CPL(s) == 0) {
- mask |= IF_MASK | IOPL_MASK;
- } else if (CPL(s) <= IOPL(s)) {
- mask |= IF_MASK;
- }
- if (dflag == MO_16) {
- mask &= 0xffff;
- }
-
- ot = gen_pop_T0(s);
- gen_helper_write_eflags(tcg_env, s->T0, tcg_constant_i32(mask));
- gen_pop_update(s, ot);
- set_cc_op(s, CC_OP_EFLAGS);
- /* abort translation because TF/AC flag may change */
- s->base.is_jmp = DISAS_EOB_NEXT;
- }
- break;
- case 0x9e: /* sahf */
- if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM))
- goto illegal_op;
- tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8);
- gen_compute_eflags(s);
- tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O);
- tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C);
- tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0);
- break;
- case 0x9f: /* lahf */
- if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM))
- goto illegal_op;
- gen_compute_eflags(s);
- /* Note: gen_compute_eflags() only gives the condition codes */
- tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02);
- tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8);
- break;
- case 0xf5: /* cmc */
- gen_compute_eflags(s);
- tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C);
- break;
- case 0xf8: /* clc */
- gen_compute_eflags(s);
- tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C);
- break;
- case 0xf9: /* stc */
- gen_compute_eflags(s);
- tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C);
- break;
- case 0xfc: /* cld */
- tcg_gen_movi_i32(s->tmp2_i32, 1);
- tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df));
- break;
- case 0xfd: /* std */
- tcg_gen_movi_i32(s->tmp2_i32, -1);
- tcg_gen_st_i32(s->tmp2_i32, tcg_env, offsetof(CPUX86State, df));
- break;
-
- /************************/
/* bit operations */
case 0x1ba: /* bt/bts/btr/btc Gv, im */
ot = dflag;
@@ -5488,188 +3497,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
}
gen_op_mov_reg_v(s, ot, reg, s->T0);
break;
- /************************/
- /* bcd */
- case 0x27: /* daa */
- if (CODE64(s))
- goto illegal_op;
- gen_update_cc_op(s);
- gen_helper_daa(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x2f: /* das */
- if (CODE64(s))
- goto illegal_op;
- gen_update_cc_op(s);
- gen_helper_das(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x37: /* aaa */
- if (CODE64(s))
- goto illegal_op;
- gen_update_cc_op(s);
- gen_helper_aaa(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0x3f: /* aas */
- if (CODE64(s))
- goto illegal_op;
- gen_update_cc_op(s);
- gen_helper_aas(tcg_env);
- set_cc_op(s, CC_OP_EFLAGS);
- break;
- case 0xd4: /* aam */
- if (CODE64(s))
- goto illegal_op;
- val = x86_ldub_code(env, s);
- if (val == 0) {
- gen_exception(s, EXCP00_DIVZ);
- } else {
- gen_helper_aam(tcg_env, tcg_constant_i32(val));
- set_cc_op(s, CC_OP_LOGICB);
- }
- break;
- case 0xd5: /* aad */
- if (CODE64(s))
- goto illegal_op;
- val = x86_ldub_code(env, s);
- gen_helper_aad(tcg_env, tcg_constant_i32(val));
- set_cc_op(s, CC_OP_LOGICB);
- break;
- /************************/
- /* misc */
- case 0x90: /* nop */
- /* XXX: correct lock test for all insn */
- if (prefixes & PREFIX_LOCK) {
- goto illegal_op;
- }
- /* If REX_B is set, then this is xchg eax, r8d, not a nop. */
- if (REX_B(s)) {
- goto do_xchg_reg_eax;
- }
- if (prefixes & PREFIX_REPZ) {
- gen_update_cc_op(s);
- gen_update_eip_cur(s);
- gen_helper_pause(tcg_env, cur_insn_len_i32(s));
- s->base.is_jmp = DISAS_NORETURN;
- }
- break;
- case 0x9b: /* fwait */
- if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) ==
- (HF_MP_MASK | HF_TS_MASK)) {
- gen_exception(s, EXCP07_PREX);
- } else {
- /* needs to be treated as I/O because of ferr_irq */
- translator_io_start(&s->base);
- gen_helper_fwait(tcg_env);
- }
- break;
- case 0xcc: /* int3 */
- gen_interrupt(s, EXCP03_INT3);
- break;
- case 0xcd: /* int N */
- val = x86_ldub_code(env, s);
- if (check_vm86_iopl(s)) {
- gen_interrupt(s, val);
- }
- break;
- case 0xce: /* into */
- if (CODE64(s))
- goto illegal_op;
- gen_update_cc_op(s);
- gen_update_eip_cur(s);
- gen_helper_into(tcg_env, cur_insn_len_i32(s));
- break;
-#ifdef WANT_ICEBP
- case 0xf1: /* icebp (undocumented, exits to external debugger) */
- gen_svm_check_intercept(s, SVM_EXIT_ICEBP);
- gen_debug(s);
- break;
-#endif
- case 0xfa: /* cli */
- if (check_iopl(s)) {
- gen_reset_eflags(s, IF_MASK);
- }
- break;
- case 0xfb: /* sti */
- if (check_iopl(s)) {
- gen_set_eflags(s, IF_MASK);
- /* interruptions are enabled only the first insn after sti */
- gen_update_eip_next(s);
- gen_eob_inhibit_irq(s, true);
- }
- break;
- case 0x62: /* bound */
- if (CODE64(s))
- goto illegal_op;
- ot = dflag;
- modrm = x86_ldub_code(env, s);
- reg = (modrm >> 3) & 7;
- mod = (modrm >> 6) & 3;
- if (mod == 3)
- goto illegal_op;
- gen_op_mov_v_reg(s, ot, s->T0, reg);
- gen_lea_modrm(env, s, modrm);
- tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0);
- if (ot == MO_16) {
- gen_helper_boundw(tcg_env, s->A0, s->tmp2_i32);
- } else {
- gen_helper_boundl(tcg_env, s->A0, s->tmp2_i32);
- }
- break;
- case 0x1c8 ... 0x1cf: /* bswap reg */
- reg = (b & 7) | REX_B(s);
-#ifdef TARGET_X86_64
- if (dflag == MO_64) {
- tcg_gen_bswap64_i64(cpu_regs[reg], cpu_regs[reg]);
- break;
- }
-#endif
- tcg_gen_bswap32_tl(cpu_regs[reg], cpu_regs[reg], TCG_BSWAP_OZ);
- break;
- case 0xd6: /* salc */
- if (CODE64(s))
- goto illegal_op;
- gen_compute_eflags_c(s, s->T0);
- tcg_gen_neg_tl(s->T0, s->T0);
- gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0);
- break;
- case 0xe0: /* loopnz */
- case 0xe1: /* loopz */
- case 0xe2: /* loop */
- case 0xe3: /* jecxz */
- {
- TCGLabel *l1, *l2;
- int diff = (int8_t)insn_get(env, s, MO_8);
-
- l1 = gen_new_label();
- l2 = gen_new_label();
- gen_update_cc_op(s);
- b &= 3;
- switch(b) {
- case 0: /* loopnz */
- case 1: /* loopz */
- gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
- gen_op_jz_ecx(s, l2);
- gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1);
- break;
- case 2: /* loop */
- gen_op_add_reg_im(s, s->aflag, R_ECX, -1);
- gen_op_jnz_ecx(s, l1);
- break;
- default:
- case 3: /* jcxz */
- gen_op_jz_ecx(s, l1);
- break;
- }
-
- gen_set_label(l2);
- gen_jmp_rel_csize(s, 0, 1);
-
- gen_set_label(l1);
- gen_jmp_rel(s, dflag, diff, 0);
- }
- break;
case 0x130: /* wrmsr */
case 0x132: /* rdmsr */
if (check_cpl0(s)) {
@@ -5730,7 +3557,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
/* TF handling for the syscall insn is different. The TF bit is checked
after the syscall insn completes. This allows #DB to not be
generated after one has entered CPL0 if TF is set in FMASK. */
- gen_eob_worker(s, false, true);
+ gen_eob_syscall(s);
break;
case 0x107: /* sysret */
/* For Intel SYSRET is only valid in long mode */
@@ -5749,7 +3576,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
checked after the sysret insn completes. This allows #DB to be
generated "as if" the syscall insn in userspace has just
completed. */
- gen_eob_worker(s, false, true);
+ gen_eob_syscall(s);
}
break;
case 0x1a2: /* cpuid */
@@ -5757,14 +3584,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
gen_update_eip_cur(s);
gen_helper_cpuid(tcg_env);
break;
- case 0xf4: /* hlt */
- if (check_cpl0(s)) {
- gen_update_cc_op(s);
- gen_update_eip_cur(s);
- gen_helper_hlt(tcg_env, cur_insn_len_i32(s));
- s->base.is_jmp = DISAS_NORETURN;
- }
- break;
case 0x100:
modrm = x86_ldub_code(env, s);
mod = (modrm >> 6) & 3;
@@ -6085,7 +3904,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
case 0xee: /* rdpkru */
- if (prefixes & PREFIX_LOCK) {
+ if (s->prefix & (PREFIX_LOCK | PREFIX_DATA
+ | PREFIX_REPZ | PREFIX_REPNZ)) {
goto illegal_op;
}
tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]);
@@ -6093,7 +3913,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64);
break;
case 0xef: /* wrpkru */
- if (prefixes & PREFIX_LOCK) {
+ if (s->prefix & (PREFIX_LOCK | PREFIX_DATA
+ | PREFIX_REPZ | PREFIX_REPNZ)) {
goto illegal_op;
}
tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX],
@@ -6169,72 +3990,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
/* nothing to do */
}
break;
- case 0x63: /* arpl or movslS (x86_64) */
-#ifdef TARGET_X86_64
- if (CODE64(s)) {
- int d_ot;
- /* d_ot is the size of destination */
- d_ot = dflag;
-
- modrm = x86_ldub_code(env, s);
- reg = ((modrm >> 3) & 7) | REX_R(s);
- mod = (modrm >> 6) & 3;
- rm = (modrm & 7) | REX_B(s);
-
- if (mod == 3) {
- gen_op_mov_v_reg(s, MO_32, s->T0, rm);
- /* sign extend */
- if (d_ot == MO_64) {
- tcg_gen_ext32s_tl(s->T0, s->T0);
- }
- gen_op_mov_reg_v(s, d_ot, reg, s->T0);
- } else {
- gen_lea_modrm(env, s, modrm);
- gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0);
- gen_op_mov_reg_v(s, d_ot, reg, s->T0);
- }
- } else
-#endif
- {
- TCGLabel *label1;
- TCGv t0, t1, t2;
-
- if (!PE(s) || VM86(s))
- goto illegal_op;
- t0 = tcg_temp_new();
- t1 = tcg_temp_new();
- t2 = tcg_temp_new();
- ot = MO_16;
- modrm = x86_ldub_code(env, s);
- reg = (modrm >> 3) & 7;
- mod = (modrm >> 6) & 3;
- rm = modrm & 7;
- if (mod != 3) {
- gen_lea_modrm(env, s, modrm);
- gen_op_ld_v(s, ot, t0, s->A0);
- } else {
- gen_op_mov_v_reg(s, ot, t0, rm);
- }
- gen_op_mov_v_reg(s, ot, t1, reg);
- tcg_gen_andi_tl(s->tmp0, t0, 3);
- tcg_gen_andi_tl(t1, t1, 3);
- tcg_gen_movi_tl(t2, 0);
- label1 = gen_new_label();
- tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1);
- tcg_gen_andi_tl(t0, t0, ~3);
- tcg_gen_or_tl(t0, t0, t1);
- tcg_gen_movi_tl(t2, CC_Z);
- gen_set_label(label1);
- if (mod != 3) {
- gen_op_st_v(s, ot, t0, s->A0);
- } else {
- gen_op_mov_reg_v(s, ot, rm, t0);
- }
- gen_compute_eflags(s);
- tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z);
- tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2);
- }
- break;
case 0x102: /* lar */
case 0x103: /* lsl */
{
@@ -6261,25 +4016,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
set_cc_op(s, CC_OP_EFLAGS);
}
break;
- case 0x118:
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- op = (modrm >> 3) & 7;
- switch(op) {
- case 0: /* prefetchnta */
- case 1: /* prefetchnt0 */
- case 2: /* prefetchnt0 */
- case 3: /* prefetchnt0 */
- if (mod == 3)
- goto illegal_op;
- gen_nop_modrm(env, s, modrm);
- /* nothing more to do */
- break;
- default: /* nop (multi byte) */
- gen_nop_modrm(env, s, modrm);
- break;
- }
- break;
case 0x11a:
modrm = x86_ldub_code(env, s);
if (s->flags & HF_MPX_EN_MASK) {
@@ -6471,10 +4207,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
}
gen_nop_modrm(env, s, modrm);
break;
- case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */
- modrm = x86_ldub_code(env, s);
- gen_nop_modrm(env, s, modrm);
- break;
case 0x120: /* mov reg, crN */
case 0x122: /* mov crN, reg */
@@ -6561,18 +4293,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
}
break;
/* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */
- case 0x1c3: /* MOVNTI reg, mem */
- if (!(s->cpuid_features & CPUID_SSE2))
- goto illegal_op;
- ot = mo_64_32(dflag);
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- if (mod == 3)
- goto illegal_op;
- reg = ((modrm >> 3) & 7) | REX_R(s);
- /* generate a generic store */
- gen_ldst_modrm(env, s, modrm, ot, reg, 1);
- break;
case 0x1ae:
modrm = x86_ldub_code(env, s);
switch (modrm) {
@@ -6733,17 +4453,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
}
goto unknown_op;
- case 0xf8: /* sfence / pcommit */
- if (prefixes & PREFIX_DATA) {
- /* pcommit */
- if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT)
- || (prefixes & PREFIX_LOCK)) {
- goto illegal_op;
- }
- break;
- }
- /* fallthru */
- case 0xf9 ... 0xff: /* sfence */
+ case 0xf8 ... 0xff: /* sfence */
if (!(s->cpuid_features & CPUID_SSE)
|| (prefixes & PREFIX_LOCK)) {
goto illegal_op;
@@ -6770,13 +4480,6 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
}
break;
- case 0x10d: /* 3DNow! prefetch(w) */
- modrm = x86_ldub_code(env, s);
- mod = (modrm >> 6) & 3;
- if (mod == 3)
- goto illegal_op;
- gen_nop_modrm(env, s, modrm);
- break;
case 0x1aa: /* rsm */
gen_svm_check_intercept(s, SVM_EXIT_RSM);
if (!(s->flags & HF_SMM_MASK))
@@ -6801,12 +4504,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
modrm = x86_ldub_code(env, s);
reg = ((modrm >> 3) & 7) | REX_R(s);
- if (s->prefix & PREFIX_DATA) {
- ot = MO_16;
- } else {
- ot = mo_64_32(dflag);
- }
-
+ ot = dflag;
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0);
gen_extu(ot, s->T0);
tcg_gen_mov_tl(cpu_cc_src, s->T0);
@@ -6815,28 +4513,21 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
set_cc_op(s, CC_OP_POPCNT);
break;
- case 0x10e ... 0x117:
- case 0x128 ... 0x12f:
- case 0x138 ... 0x13a:
- case 0x150 ... 0x179:
- case 0x17c ... 0x17f:
- case 0x1c2:
- case 0x1c4 ... 0x1c6:
- case 0x1d0 ... 0x1fe:
- disas_insn_new(s, cpu, b);
- break;
default:
- goto unknown_op;
+ g_assert_not_reached();
}
- return true;
+ return;
illegal_op:
gen_illegal_opcode(s);
- return true;
+ return;
unknown_op:
gen_unknown_opcode(env, s);
- return true;
}
+#include "decode-new.h"
+#include "emit.c.inc"
+#include "decode-new.c.inc"
+
void tcg_x86_init(void)
{
static const char reg_names[CPU_NB_REGS][4] = {
@@ -6958,7 +4649,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
dc->cc_op = CC_OP_DYNAMIC;
dc->cc_op_dirty = false;
- dc->popl_esp_hack = 0;
/* select memory access functions */
dc->mem_index = cpu_mmu_index(cpu, false);
dc->cpuid_features = env->features[FEAT_1_EDX];
@@ -7010,6 +4700,9 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
+ bool orig_cc_op_dirty = dc->cc_op_dirty;
+ CCOp orig_cc_op = dc->cc_op;
+ target_ulong orig_pc_save = dc->pc_save;
#ifdef TARGET_VSYSCALL_PAGE
/*
@@ -7022,23 +4715,51 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
}
#endif
- if (disas_insn(dc, cpu)) {
- target_ulong pc_next = dc->pc;
- dc->base.pc_next = pc_next;
+ switch (sigsetjmp(dc->jmpbuf, 0)) {
+ case 0:
+ disas_insn(dc, cpu);
+ break;
+ case 1:
+ gen_exception_gpf(dc);
+ break;
+ case 2:
+ /* Restore state that may affect the next instruction. */
+ dc->pc = dc->base.pc_next;
+ /*
+ * TODO: These save/restore can be removed after the table-based
+ * decoder is complete; we will be decoding the insn completely
+ * before any code generation that might affect these variables.
+ */
+ dc->cc_op_dirty = orig_cc_op_dirty;
+ dc->cc_op = orig_cc_op;
+ dc->pc_save = orig_pc_save;
+ /* END TODO */
+ dc->base.num_insns--;
+ tcg_remove_ops_after(dc->prev_insn_end);
+ dc->base.insn_start = dc->prev_insn_start;
+ dc->base.is_jmp = DISAS_TOO_MANY;
+ return;
+ default:
+ g_assert_not_reached();
+ }
- if (dc->base.is_jmp == DISAS_NEXT) {
- if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) {
- /*
- * If single step mode, we generate only one instruction and
- * generate an exception.
- * If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
- * the flag and abort the translation to give the irqs a
- * chance to happen.
- */
- dc->base.is_jmp = DISAS_EOB_NEXT;
- } else if (!is_same_page(&dc->base, pc_next)) {
- dc->base.is_jmp = DISAS_TOO_MANY;
- }
+ /*
+ * Instruction decoding completed (possibly with #GP if the
+ * 15-byte boundary was exceeded).
+ */
+ dc->base.pc_next = dc->pc;
+ if (dc->base.is_jmp == DISAS_NEXT) {
+ if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) {
+ /*
+ * If single step mode, we generate only one instruction and
+ * generate an exception.
+ * If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
+ * the flag and abort the translation to give the irqs a
+ * chance to happen.
+ */
+ dc->base.is_jmp = DISAS_EOB_NEXT;
+ } else if (!is_same_page(&dc->base, dc->base.pc_next)) {
+ dc->base.is_jmp = DISAS_TOO_MANY;
}
}
}
@@ -7064,7 +4785,7 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
case DISAS_EOB_INHIBIT_IRQ:
gen_update_cc_op(dc);
gen_update_eip_cur(dc);
- gen_eob_inhibit_irq(dc, true);
+ gen_eob_inhibit_irq(dc);
break;
case DISAS_JUMP:
gen_jr(dc);
@@ -7074,22 +4795,12 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void i386_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- DisasContext *dc = container_of(dcbase, DisasContext, base);
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first));
- target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size);
-}
-
static const TranslatorOps i386_tr_ops = {
.init_disas_context = i386_tr_init_disas_context,
.tb_start = i386_tr_tb_start,
.insn_start = i386_tr_insn_start,
.translate_insn = i386_tr_translate_insn,
.tb_stop = i386_tr_tb_stop,
- .disas_log = i386_tr_disas_log,
};
/* generate intermediate code for basic block 'tb'. */
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index b08e644517..a6674a826d 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2236,7 +2236,7 @@ int whpx_init_vcpu(CPUState *cpu)
}
vcpu->interruptable = true;
- cpu->accel->dirty = true;
+ vcpu->dirty = true;
cpu->accel = vcpu;
max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index bac84dca7a..a0cad53676 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -92,7 +92,7 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env,
{
CPUState *cs = env_cpu(env);
- qemu_log_mask(CPU_LOG_INT, "%s: expection: %d (%s)\n",
+ qemu_log_mask(CPU_LOG_INT, "%s: exception: %d (%s)\n",
__func__,
exception,
loongarch_exception_name(exception));
@@ -336,7 +336,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
set_pc(cpu_env(cs), tb->pc);
}
@@ -505,7 +505,9 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
lacc->parent_phases.hold(obj, type);
}
+#ifdef CONFIG_TCG
env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3;
+#endif
env->fcsr0 = 0x0;
int n;
@@ -550,7 +552,9 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type)
#ifndef CONFIG_USER_ONLY
env->pc = 0x1c000000;
+#ifdef CONFIG_TCG
memset(env->tlb, 0, sizeof(env->tlb));
+#endif
if (kvm_enabled()) {
kvm_arch_reset_vcpu(env);
}
@@ -686,8 +690,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
int i;
qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc);
- qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0,
- get_float_exception_flags(&env->fp_status));
+ qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0);
/* gpr */
for (i = 0; i < 32; i++) {
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index abb01b2cc7..41b8e6d96d 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -270,6 +270,7 @@ union fpr_t {
VReg vreg;
};
+#ifdef CONFIG_TCG
struct LoongArchTLB {
uint64_t tlb_misc;
/* Fields corresponding to CSR_TLBELO0/1 */
@@ -277,23 +278,18 @@ struct LoongArchTLB {
uint64_t tlb_entry1;
};
typedef struct LoongArchTLB LoongArchTLB;
+#endif
typedef struct CPUArchState {
uint64_t gpr[32];
uint64_t pc;
fpr_t fpr[32];
- float_status fp_status;
bool cf[8];
-
uint32_t fcsr0;
- uint32_t fcsr0_mask;
uint32_t cpucfg[21];
- uint64_t lladdr; /* LL virtual address compared against SC */
- uint64_t llval;
-
/* LoongArch CSRs */
uint64_t CSR_CRMD;
uint64_t CSR_PRMD;
@@ -350,8 +346,16 @@ typedef struct CPUArchState {
uint64_t CSR_DERA;
uint64_t CSR_DSAVE;
+#ifdef CONFIG_TCG
+ float_status fp_status;
+ uint32_t fcsr0_mask;
+ uint64_t lladdr; /* LL virtual address compared against SC */
+ uint64_t llval;
+#endif
#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_TCG
LoongArchTLB tlb[LOONGARCH_TLB_MAX];
+#endif
AddressSpace *address_space_iocsr;
bool load_elf;
@@ -359,6 +363,8 @@ typedef struct CPUArchState {
uint32_t mp_state;
/* Store ipistate to access from this struct */
DeviceState *ipistate;
+
+ struct loongarch_boot_info *boot_info;
#endif
} CPULoongArchState;
diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c
index 960eec9567..580362ac3e 100644
--- a/target/loongarch/cpu_helper.c
+++ b/target/loongarch/cpu_helper.c
@@ -11,6 +11,7 @@
#include "internals.h"
#include "cpu-csr.h"
+#ifdef CONFIG_TCG
static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical,
int *prot, target_ulong address,
int access_type, int index, int mmu_idx)
@@ -154,6 +155,14 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
return TLBRET_NOMATCH;
}
+#else
+static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical,
+ int *prot, target_ulong address,
+ MMUAccessType access_type, int mmu_idx)
+{
+ return TLBRET_NOMATCH;
+}
+#endif
static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va,
target_ulong dmw)
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index 8224d94333..bc75552d0f 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -587,22 +587,22 @@ int kvm_arch_get_registers(CPUState *cs)
return ret;
}
- ret = kvm_loongarch_get_csr(cs);
+ ret = kvm_loongarch_get_cpucfg(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_get_regs_fp(cs);
+ ret = kvm_loongarch_get_csr(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_get_mpstate(cs);
+ ret = kvm_loongarch_get_regs_fp(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_get_cpucfg(cs);
+ ret = kvm_loongarch_get_mpstate(cs);
return ret;
}
@@ -615,22 +615,22 @@ int kvm_arch_put_registers(CPUState *cs, int level)
return ret;
}
- ret = kvm_loongarch_put_csr(cs, level);
+ ret = kvm_loongarch_put_cpucfg(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_put_regs_fp(cs);
+ ret = kvm_loongarch_put_csr(cs, level);
if (ret) {
return ret;
}
- ret = kvm_loongarch_put_mpstate(cs);
+ ret = kvm_loongarch_put_regs_fp(cs);
if (ret) {
return ret;
}
- ret = kvm_loongarch_put_cpucfg(cs);
+ ret = kvm_loongarch_put_mpstate(cs);
return ret;
}
diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c
index c7029fb9b4..9cd9e848d6 100644
--- a/target/loongarch/machine.c
+++ b/target/loongarch/machine.c
@@ -8,6 +8,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "migration/cpu.h"
+#include "sysemu/tcg.h"
#include "vec.h"
static const VMStateDescription vmstate_fpu_reg = {
@@ -109,9 +110,15 @@ static const VMStateDescription vmstate_lasx = {
},
};
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+static bool tlb_needed(void *opaque)
+{
+ return tcg_enabled();
+}
+
/* TLB state */
-const VMStateDescription vmstate_tlb = {
- .name = "cpu/tlb",
+static const VMStateDescription vmstate_tlb_entry = {
+ .name = "cpu/tlb_entry",
.version_id = 0,
.minimum_version_id = 0,
.fields = (const VMStateField[]) {
@@ -122,6 +129,19 @@ const VMStateDescription vmstate_tlb = {
}
};
+static const VMStateDescription vmstate_tlb = {
+ .name = "cpu/tlb",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = tlb_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
+ 0, vmstate_tlb_entry, LoongArchTLB),
+ VMSTATE_END_OF_LIST()
+ }
+};
+#endif
+
/* LoongArch CPU state */
const VMStateDescription vmstate_loongarch_cpu = {
.name = "cpu",
@@ -187,9 +207,6 @@ const VMStateDescription vmstate_loongarch_cpu = {
VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU),
VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU),
VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU),
- /* TLB */
- VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX,
- 0, vmstate_tlb, LoongArchTLB),
VMSTATE_END_OF_LIST()
},
@@ -197,6 +214,9 @@ const VMStateDescription vmstate_loongarch_cpu = {
&vmstate_fpu,
&vmstate_lsx,
&vmstate_lasx,
+#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY)
+ &vmstate_tlb,
+#endif
NULL
}
};
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
index 57f5308632..d6331f9b0b 100644
--- a/target/loongarch/tcg/tlb_helper.c
+++ b/target/loongarch/tcg/tlb_helper.c
@@ -13,6 +13,7 @@
#include "internals.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/cpu_ldst.h"
#include "exec/log.h"
#include "cpu-csr.h"
diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c
index 7567712655..1fca4afc73 100644
--- a/target/loongarch/tcg/translate.c
+++ b/target/loongarch/tcg/translate.c
@@ -325,20 +325,12 @@ static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void loongarch_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps loongarch_tr_ops = {
.init_disas_context = loongarch_tr_init_disas_context,
.tb_start = loongarch_tr_tb_start,
.insn_start = loongarch_tr_insn_start,
.translate_insn = loongarch_tr_translate_insn,
.tb_stop = loongarch_tr_tb_stop,
- .disas_log = loongarch_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 7a91f33b17..7967ad13cb 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/gdbstub.h"
#include "exec/helper-proto.h"
#include "gdbstub/helpers.h"
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 169927552a..445966fb6a 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "qemu/log.h"
@@ -6105,20 +6104,12 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void m68k_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps m68k_tr_ops = {
.init_disas_context = m68k_tr_init_disas_context,
.tb_start = m68k_tr_tb_start,
.insn_start = m68k_tr_insn_start,
.translate_insn = m68k_tr_translate_insn,
.tb_stop = m68k_tr_tb_stop,
- .disas_log = m68k_tr_disas_log,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
diff --git a/target/microblaze/Kconfig b/target/microblaze/Kconfig
index a5410d9218..e91d58d88f 100644
--- a/target/microblaze/Kconfig
+++ b/target/microblaze/Kconfig
@@ -1,2 +1,3 @@
config MICROBLAZE
bool
+ select DEVICE_TREE # needed by boot.c
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index 9eb7374ccd..41ad47d04c 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -99,7 +99,7 @@ static void mb_cpu_synchronize_from_tb(CPUState *cs,
{
MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu->env.pc = tb->pc;
cpu->env.iflags = tb->flags & IFLAGS_TB_MASK;
}
diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c
index d25c9eb4d3..5d3259ce31 100644
--- a/target/microblaze/helper.c
+++ b/target/microblaze/helper.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "qemu/host-utils.h"
#include "exec/log.h"
@@ -51,7 +52,7 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (mmu_idx == MMU_NOMMU_IDX) {
/* MMU disabled or not available. */
address &= TARGET_PAGE_MASK;
- prot = PAGE_BITS;
+ prot = PAGE_RWX;
tlb_set_page_with_attrs(cs, address, address, attrs, prot, mmu_idx,
TARGET_PAGE_SIZE);
return true;
diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c
index 234006634e..2423ac6172 100644
--- a/target/microblaze/mmu.c
+++ b/target/microblaze/mmu.c
@@ -22,6 +22,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
static unsigned int tlb_decode_size(unsigned int f)
{
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c
index 6d89c1a175..4beaf69e76 100644
--- a/target/microblaze/translate.c
+++ b/target/microblaze/translate.c
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "tcg/tcg-op.h"
@@ -1637,7 +1636,7 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs)
dc->tb_flags_to_set = 0;
- ir = cpu_ldl_code(cpu_env(cs), dc->base.pc_next);
+ ir = translator_ldl(cpu_env(cs), &dc->base, dc->base.pc_next);
if (!decode(dc, ir)) {
trap_illegal(dc, true);
}
@@ -1771,20 +1770,12 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
}
}
-static void mb_tr_disas_log(const DisasContextBase *dcb,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcb->pc_first));
- target_disas(logfile, cs, dcb->pc_first, dcb->tb->size);
-}
-
static const TranslatorOps mb_tr_ops = {
.init_disas_context = mb_tr_init_disas_context,
.tb_start = mb_tr_tb_start,
.insn_start = mb_tr_insn_start,
.translate_insn = mb_tr_translate_insn,
.tb_stop = mb_tr_tb_stop,
- .disas_log = mb_tr_disas_log,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
diff --git a/target/mips/sysemu/physaddr.c b/target/mips/sysemu/physaddr.c
index 5c5184e136..505781d84c 100644
--- a/target/mips/sysemu/physaddr.c
+++ b/target/mips/sysemu/physaddr.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "../internal.h"
static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx)
diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c
index 13275d1ded..4886d087b2 100644
--- a/target/mips/tcg/exception.c
+++ b/target/mips/tcg/exception.c
@@ -81,7 +81,7 @@ void mips_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb)
{
CPUMIPSState *env = cpu_env(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
env->active_tc.PC = tb->pc;
env->hflags &= ~MIPS_HFLAG_BMASK;
env->hflags |= tb->flags & MIPS_HFLAG_BMASK;
diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c
index 5baa25348e..9ce5e2ceac 100644
--- a/target/mips/tcg/sysemu/special_helper.c
+++ b/target/mips/tcg/sysemu/special_helper.c
@@ -93,7 +93,7 @@ bool mips_io_recompile_replay_branch(CPUState *cs, const TranslationBlock *tb)
CPUMIPSState *env = cpu_env(cs);
if ((env->hflags & MIPS_HFLAG_BMASK) != 0
- && !(cs->tcg_cflags & CF_PCREL) && env->active_tc.PC != tb->pc) {
+ && !tcg_cflags_has(cs, CF_PCREL) && env->active_tc.PC != tb->pc) {
env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
env->hflags &= ~MIPS_HFLAG_BMASK;
return true;
diff --git a/target/mips/tcg/sysemu/tlb_helper.c b/target/mips/tcg/sysemu/tlb_helper.c
index 119eae771e..3ba6d369a6 100644
--- a/target/mips/tcg/sysemu/tlb_helper.c
+++ b/target/mips/tcg/sysemu/tlb_helper.c
@@ -22,6 +22,7 @@
#include "cpu.h"
#include "internal.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/cpu_ldst.h"
#include "exec/log.h"
#include "exec/helper-proto.h"
diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index 06c108cc9c..333469b268 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -29,7 +29,6 @@
#include "exec/translation-block.h"
#include "semihosting/semihost.h"
#include "trace.h"
-#include "disas/disas.h"
#include "fpu_helper.h"
#define HELPER_H "helper.h"
@@ -15475,20 +15474,12 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void mips_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps mips_tr_ops = {
.init_disas_context = mips_tr_init_disas_context,
.tb_start = mips_tr_tb_start,
.insn_start = mips_tr_insn_start,
.translate_insn = mips_tr_translate_insn,
.tb_stop = mips_tr_tb_stop,
- .disas_log = mips_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/openrisc/Kconfig b/target/openrisc/Kconfig
index e0da4ac1df..cd66c2e3b6 100644
--- a/target/openrisc/Kconfig
+++ b/target/openrisc/Kconfig
@@ -1,2 +1,3 @@
config OPENRISC
bool
+ select DEVICE_TREE # needed by boot.c
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
index d711035cf5..fdaaa09fc8 100644
--- a/target/openrisc/cpu.c
+++ b/target/openrisc/cpu.c
@@ -45,7 +45,7 @@ static void openrisc_cpu_synchronize_from_tb(CPUState *cs,
{
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu->env.pc = tb->pc;
}
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
index 603c26715e..c632d5230b 100644
--- a/target/openrisc/mmu.c
+++ b/target/openrisc/mmu.c
@@ -22,6 +22,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "gdbstub/helpers.h"
#include "qemu/host-utils.h"
#include "hw/loader.h"
diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
index 23fff46084..ca566847cb 100644
--- a/target/openrisc/translate.c
+++ b/target/openrisc/translate.c
@@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
-#include "disas/disas.h"
#include "tcg/tcg-op.h"
#include "qemu/log.h"
#include "qemu/bitops.h"
@@ -1638,22 +1637,12 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void openrisc_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- DisasContext *s = container_of(dcbase, DisasContext, base);
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(s->base.pc_first));
- target_disas(logfile, cs, s->base.pc_first, s->base.tb->size);
-}
-
static const TranslatorOps openrisc_tr_ops = {
.init_disas_context = openrisc_tr_init_disas_context,
.tb_start = openrisc_tr_tb_start,
.insn_start = openrisc_tr_insn_start,
.translate_insn = openrisc_tr_translate_insn,
.tb_stop = openrisc_tr_tb_stop,
- .disas_log = openrisc_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/ppc/Kconfig b/target/ppc/Kconfig
index 3ff152051a..0283711673 100644
--- a/target/ppc/Kconfig
+++ b/target/ppc/Kconfig
@@ -3,3 +3,4 @@ config PPC
config PPC64
bool
+ select PPC
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 6d82f24c87..c11a69fd90 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -7063,7 +7063,7 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data)
}
name = cpu_model_from_type(typename);
- qemu_printf("PowerPC %-16s PVR %08x\n", name, pcc->pvr);
+ qemu_printf(" %-16s PVR %08x\n", name, pcc->pvr);
for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) {
PowerPCCPUAlias *alias = &ppc_cpu_aliases[i];
ObjectClass *alias_oc = ppc_cpu_class_by_name(alias->model);
@@ -7076,10 +7076,10 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data)
* avoid printing the wrong alias here and use "preferred" instead
*/
if (strcmp(alias->alias, family->desc) == 0) {
- qemu_printf("PowerPC %-16s (alias for preferred %s CPU)\n",
+ qemu_printf(" %-16s (alias for preferred %s CPU)\n",
alias->alias, family->desc);
} else {
- qemu_printf("PowerPC %-16s (alias for %s)\n",
+ qemu_printf(" %-16s (alias for %s)\n",
alias->alias, name);
}
}
@@ -7090,6 +7090,7 @@ void ppc_cpu_list(void)
{
GSList *list;
+ qemu_printf("Available CPUs:\n");
list = object_class_get_list(TYPE_POWERPC_CPU, false);
list = g_slist_sort(list, ppc_cpu_list_compare);
g_slist_foreach(list, ppc_cpu_list_entry, NULL);
@@ -7097,7 +7098,7 @@ void ppc_cpu_list(void)
#ifdef CONFIG_KVM
qemu_printf("\n");
- qemu_printf("PowerPC %s\n", "host");
+ qemu_printf(" %s\n", "host");
#endif
}
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 601c0b533f..98b41a970c 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -20,6 +20,7 @@
#include "exec/breakpoint.h"
#include "hw/registerfields.h"
+#include "exec/page-protection.h"
/* PM instructions */
typedef enum {
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 63930d4a77..46fccff786 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -49,6 +49,8 @@
#include "elf.h"
#include "sysemu/kvm_int.h"
+#include CONFIG_DEVICES
+
#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/"
#define DEBUG_RETURN_GUEST 0
@@ -71,7 +73,6 @@ static int cap_hior;
static int cap_one_reg;
static int cap_epr;
static int cap_ppc_watchdog;
-static int cap_papr;
static int cap_htab_fd;
static int cap_fixup_hcalls;
static int cap_htm; /* Hardware transactional memory support */
@@ -90,6 +91,12 @@ static int cap_fwnmi;
static int cap_rpt_invalidate;
static int cap_ail_mode_3;
+#ifdef CONFIG_PSERIES
+static int cap_papr;
+#else
+#define cap_papr (0)
+#endif
+
static uint32_t debug_inst_opcode;
/*
@@ -1668,7 +1675,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
trace_kvm_handle_halt();
ret = kvmppc_handle_halt(cpu);
break;
-#if defined(TARGET_PPC64)
+#if defined(CONFIG_PSERIES)
case KVM_EXIT_PAPR_HCALL:
trace_kvm_handle_papr_hcall(run->papr_hcall.nr);
run->papr_hcall.ret = spapr_hypercall(cpu,
@@ -1698,7 +1705,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ret = 0;
break;
-#if defined(TARGET_PPC64)
+#if defined(CONFIG_PSERIES)
case KVM_EXIT_NMI:
trace_kvm_handle_nmi_exception();
ret = kvm_handle_nmi(cpu, run);
@@ -2054,6 +2061,7 @@ void kvmppc_enable_h_rpt_invalidate(void)
kvmppc_enable_hcall(kvm_state, H_RPT_INVALIDATE);
}
+#ifdef CONFIG_PSERIES
void kvmppc_set_papr(PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);
@@ -2075,6 +2083,7 @@ void kvmppc_set_papr(PowerPCCPU *cpu)
*/
cap_papr = 1;
}
+#endif
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr)
{
@@ -2837,7 +2846,7 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
return data & 0xffff;
}
-#if defined(TARGET_PPC64)
+#if defined(CONFIG_PSERIES)
int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run)
{
uint16_t flags = run->flags & KVM_RUN_PPC_NMI_DISP_MASK;
diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c
index 3976416840..6dfedab11d 100644
--- a/target/ppc/mmu-hash32.c
+++ b/target/ppc/mmu-hash32.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "internal.h"
diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c
index d645c0bb94..0966422a55 100644
--- a/target/ppc/mmu-hash64.c
+++ b/target/ppc/mmu-hash64.c
@@ -21,6 +21,7 @@
#include "qemu/units.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "qemu/error-report.h"
#include "qemu/qemu-print.h"
#include "sysemu/hw_accel.h"
@@ -1187,7 +1188,7 @@ void ppc_hash64_init(PowerPCCPU *cpu)
return;
}
- cpu->hash64_opts = g_memdup(pcc->hash64_opts, sizeof(*cpu->hash64_opts));
+ cpu->hash64_opts = g_memdup2(pcc->hash64_opts, sizeof(*cpu->hash64_opts));
}
void ppc_hash64_finalize(PowerPCCPU *cpu)
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
index 690dff7a49..8daf71d2db 100644
--- a/target/ppc/mmu-radix64.c
+++ b/target/ppc/mmu-radix64.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h
index 4c768aa5cc..c5c04a1527 100644
--- a/target/ppc/mmu-radix64.h
+++ b/target/ppc/mmu-radix64.h
@@ -3,6 +3,8 @@
#ifndef CONFIG_USER_ONLY
+#include "exec/page-protection.h"
+
/* Radix Quadrants */
#define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF
#define R_EADDR_VALID_MASK 0xC00FFFFFFFFFFFFF
diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c
index 751403f1c8..4fde7fd3bf 100644
--- a/target/ppc/mmu_common.c
+++ b/target/ppc/mmu_common.c
@@ -25,6 +25,7 @@
#include "mmu-hash64.h"
#include "mmu-hash32.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/log.h"
#include "helper_regs.h"
#include "qemu/error-report.h"
diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c
index c071b4d5e2..b35a93c198 100644
--- a/target/ppc/mmu_helper.c
+++ b/target/ppc/mmu_helper.c
@@ -25,6 +25,7 @@
#include "mmu-hash64.h"
#include "mmu-hash32.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/log.h"
#include "helper_regs.h"
#include "qemu/error-report.h"
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 93ffec787c..49dee6cab0 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "internal.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
@@ -7405,20 +7404,12 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void ppc_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps ppc_tr_ops = {
.init_disas_context = ppc_tr_init_disas_context,
.tb_start = ppc_tr_tb_start,
.insn_start = ppc_tr_insn_start,
.translate_insn = ppc_tr_translate_insn,
.tb_stop = ppc_tr_tb_stop,
- .disas_log = ppc_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig
index adb7de3f37..5f30df22f2 100644
--- a/target/riscv/Kconfig
+++ b/target/riscv/Kconfig
@@ -1,7 +1,9 @@
config RISCV32
bool
select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting()
+ select DEVICE_TREE # needed by boot.c
config RISCV64
bool
select ARM_COMPATIBLE_SEMIHOSTING # for do_common_semihosting()
+ select DEVICE_TREE # needed by boot.c
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index fc090d729a..8ad546a45a 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -24,6 +24,7 @@
#include "internals.h"
#include "pmu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "instmap.h"
#include "tcg/tcg-op.h"
#include "trace.h"
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index 49d2f3ad58..eaa36121c7 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -1054,8 +1054,8 @@ static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu,
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, &reg);
if (ret != 0) {
- error_report("Unable to read vlenb register, error code: %s",
- strerrorname_np(errno));
+ error_report("Unable to read vlenb register, error code: %d",
+ errno);
exit(EXIT_FAILURE);
}
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 2a76b611a0..9eea397e72 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -25,6 +25,7 @@
#include "cpu.h"
#include "trace.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
uint8_t val);
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index b5b95e052d..40054a391a 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -96,7 +96,7 @@ static void riscv_cpu_synchronize_from_tb(CPUState *cs,
CPURISCVState *env = &cpu->env;
RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
if (xl == MXL_RV32) {
env->pc = (int32_t) tb->pc;
@@ -890,7 +890,7 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp)
CPURISCVState *env = &cpu->env;
Error *local_err = NULL;
- CPU(cs)->tcg_cflags |= CF_PCREL;
+ tcg_cflags_set(CPU(cs), CF_PCREL);
if (cpu->cfg.ext_sstc) {
riscv_timer_init(cpu);
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 9ff09ebdb6..2c27fd4ce1 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -20,8 +20,6 @@
#include "qemu/log.h"
#include "cpu.h"
#include "tcg/tcg-op.h"
-#include "disas/disas.h"
-#include "exec/cpu_ldst.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
@@ -1083,7 +1081,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
CPUState *cpu = ctx->cs;
CPURISCVState *env = cpu_env(cpu);
- return cpu_ldl_code(env, pc);
+ return translator_ldl(env, &ctx->base, pc);
}
/* Include insn module translation function */
@@ -1244,7 +1242,8 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK;
if (page_ofs > TARGET_PAGE_SIZE - MAX_INSN_LEN) {
- uint16_t next_insn = cpu_lduw_code(env, ctx->base.pc_next);
+ uint16_t next_insn =
+ translator_lduw(env, &ctx->base, ctx->base.pc_next);
int len = insn_len(next_insn);
if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) {
@@ -1270,29 +1269,12 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void riscv_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
-#ifndef CONFIG_USER_ONLY
- RISCVCPU *rvcpu = RISCV_CPU(cpu);
- CPURISCVState *env = &rvcpu->env;
-#endif
-
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
-#ifndef CONFIG_USER_ONLY
- fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: %d\n",
- env->priv, env->virt_enabled);
-#endif
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps riscv_tr_ops = {
.init_disas_context = riscv_tr_init_disas_context,
.tb_start = riscv_tr_tb_start,
.insn_start = riscv_tr_insn_start,
.translate_insn = riscv_tr_translate_insn,
.tb_stop = riscv_tr_tb_stop,
- .disas_log = riscv_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index fa139040f8..1b4d5a8e37 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -23,6 +23,7 @@
#include "exec/memop.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
+#include "exec/page-protection.h"
#include "exec/helper-proto.h"
#include "fpu/softfloat.h"
#include "tcg/tcg-gvec-desc.h"
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
index e3dfb09722..8a584f0a11 100644
--- a/target/rx/cpu.c
+++ b/target/rx/cpu.c
@@ -22,6 +22,7 @@
#include "cpu.h"
#include "migration/vmstate.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "hw/loader.h"
#include "fpu/softfloat.h"
#include "tcg/debug-assert.h"
@@ -45,7 +46,7 @@ static void rx_cpu_synchronize_from_tb(CPUState *cs,
{
RXCPU *cpu = RX_CPU(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu->env.pc = tb->pc;
}
diff --git a/target/rx/translate.c b/target/rx/translate.c
index f6e9e0ec90..9b81cf20b3 100644
--- a/target/rx/translate.c
+++ b/target/rx/translate.c
@@ -22,7 +22,6 @@
#include "cpu.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
-#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
#include "exec/translator.h"
@@ -75,10 +74,10 @@ static TCGv_i64 cpu_acc;
/* decoder helper */
static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn,
- int i, int n)
+ int i, int n)
{
while (++i <= n) {
- uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++);
+ uint8_t b = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next++);
insn |= b << (32 - i * 8);
}
return insn;
@@ -90,22 +89,24 @@ static uint32_t li(DisasContext *ctx, int sz)
CPURXState *env = ctx->env;
addr = ctx->base.pc_next;
- tcg_debug_assert(sz < 4);
switch (sz) {
case 1:
ctx->base.pc_next += 1;
- return cpu_ldsb_code(env, addr);
+ return (int8_t)translator_ldub(env, &ctx->base, addr);
case 2:
ctx->base.pc_next += 2;
- return cpu_ldsw_code(env, addr);
+ return (int16_t)translator_lduw(env, &ctx->base, addr);
case 3:
ctx->base.pc_next += 3;
- tmp = cpu_ldsb_code(env, addr + 2) << 16;
- tmp |= cpu_lduw_code(env, addr) & 0xffff;
+ tmp = (int8_t)translator_ldub(env, &ctx->base, addr + 2);
+ tmp <<= 16;
+ tmp |= translator_lduw(env, &ctx->base, addr);
return tmp;
case 0:
ctx->base.pc_next += 4;
- return cpu_ldl_code(env, addr);
+ return translator_ldl(env, &ctx->base, addr);
+ default:
+ g_assert_not_reached();
}
return 0;
}
@@ -190,22 +191,22 @@ static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem,
{
uint32_t dsp;
- tcg_debug_assert(ld < 3);
switch (ld) {
case 0:
return cpu_regs[reg];
case 1:
- dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size;
+ dsp = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next) << size;
tcg_gen_addi_i32(mem, cpu_regs[reg], dsp);
ctx->base.pc_next += 1;
return mem;
case 2:
- dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size;
+ dsp = translator_lduw(ctx->env, &ctx->base, ctx->base.pc_next) << size;
tcg_gen_addi_i32(mem, cpu_regs[reg], dsp);
ctx->base.pc_next += 2;
return mem;
+ default:
+ g_assert_not_reached();
}
- return NULL;
}
static inline MemOp mi_to_mop(unsigned mi)
@@ -2247,20 +2248,12 @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void rx_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps rx_tr_ops = {
.init_disas_context = rx_tr_init_disas_context,
.tb_start = rx_tr_tb_start,
.insn_start = rx_tr_insn_start,
.translate_insn = rx_tr_translate_insn,
.tb_stop = rx_tr_tb_stop,
- .disas_log = rx_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/s390x/Kconfig b/target/s390x/Kconfig
index 72da48136c..d886be48b4 100644
--- a/target/s390x/Kconfig
+++ b/target/s390x/Kconfig
@@ -1,2 +1,4 @@
config S390X
bool
+ select PCI
+ select S390_FLIC
diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index d28eb65845..cb4e2b8920 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -212,6 +212,23 @@ void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque,
};
}
+void s390_get_deprecated_features(S390FeatBitmap features)
+{
+ static const int feats[] = {
+ /* CSSKE is deprecated on newer generations */
+ S390_FEAT_CONDITIONAL_SSKE,
+ S390_FEAT_BPB,
+ /* Deprecated on z16 */
+ S390_FEAT_CONSTRAINT_TRANSACTIONAL_EXE,
+ S390_FEAT_TRANSACTIONAL_EXE
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(feats); i++) {
+ set_bit(feats[i], features);
+ }
+}
+
#define FEAT_GROUP_INIT(_name, _group, _desc) \
{ \
.name = _name, \
diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h
index a9bd68a2e1..661a8cd6db 100644
--- a/target/s390x/cpu_features.h
+++ b/target/s390x/cpu_features.h
@@ -69,6 +69,7 @@ void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type,
uint8_t *data);
void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque,
void (*fn)(const char *name, void *opaque));
+void s390_get_deprecated_features(S390FeatBitmap features);
/* Definition of a CPU feature group */
typedef struct {
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 8ed3bb6a27..efb508cd2e 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -355,9 +355,9 @@ static void s390_print_cpu_model_list_entry(gpointer data, gpointer user_data)
/* strip off the -s390x-cpu */
g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0;
if (details->len) {
- qemu_printf("s390 %-15s %-35s (%s)\n", name, scc->desc, details->str);
+ qemu_printf(" %-15s %-35s (%s)\n", name, scc->desc, details->str);
} else {
- qemu_printf("s390 %-15s %-35s\n", name, scc->desc);
+ qemu_printf(" %-15s %-35s\n", name, scc->desc);
}
g_free(name);
}
@@ -402,6 +402,7 @@ void s390_cpu_list(void)
S390Feat feat;
GSList *list;
+ qemu_printf("Available CPUs:\n");
list = object_class_get_list(TYPE_S390_CPU, false);
list = g_slist_sort(list, s390_cpu_list_compare);
g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL);
@@ -411,14 +412,14 @@ void s390_cpu_list(void)
for (feat = 0; feat < S390_FEAT_MAX; feat++) {
const S390FeatDef *def = s390_feat_def(feat);
- qemu_printf("%-20s %s\n", def->name, def->desc);
+ qemu_printf(" %-20s %s\n", def->name, def->desc);
}
qemu_printf("\nRecognized feature groups:\n");
for (group = 0; group < S390_FEAT_GROUP_MAX; group++) {
const S390FeatGroupDef *def = s390_feat_group_def(group);
- qemu_printf("%-20s %s\n", def->name, def->desc);
+ qemu_printf(" %-20s %s\n", def->name, def->desc);
}
}
@@ -510,7 +511,7 @@ static void check_compat_model_failed(Error **errp,
return;
}
-static void check_compatibility(const S390CPUModel *max_model,
+static bool check_compatibility(const S390CPUModel *max_model,
const S390CPUModel *model, Error **errp)
{
ERRP_GUARD();
@@ -518,11 +519,11 @@ static void check_compatibility(const S390CPUModel *max_model,
if (model->def->gen > max_model->def->gen) {
check_compat_model_failed(errp, max_model, "Selected CPU generation is too new");
- return;
+ return false;
} else if (model->def->gen == max_model->def->gen &&
model->def->ec_ga > max_model->def->ec_ga) {
check_compat_model_failed(errp, max_model, "Selected CPU GA level is too new");
- return;
+ return false;
}
#ifndef CONFIG_USER_ONLY
@@ -530,14 +531,14 @@ static void check_compatibility(const S390CPUModel *max_model,
error_setg(errp, "The unpack facility is not compatible with "
"the --only-migratable option. You must remove either "
"the 'unpack' facility or the --only-migratable option");
- return;
+ return false;
}
#endif
/* detect the missing features to properly report them */
bitmap_andnot(missing, model->features, max_model->features, S390_FEAT_MAX);
if (bitmap_empty(missing, S390_FEAT_MAX)) {
- return;
+ return true;
}
error_setg(errp, " ");
@@ -546,11 +547,11 @@ static void check_compatibility(const S390CPUModel *max_model,
"available in the current configuration: ");
error_append_hint(errp,
"Consider a different accelerator, QEMU, or kernel version\n");
+ return false;
}
S390CPUModel *get_max_cpu_model(Error **errp)
{
- Error *err = NULL;
static S390CPUModel max_model;
static bool cached;
@@ -559,16 +560,14 @@ S390CPUModel *get_max_cpu_model(Error **errp)
}
if (kvm_enabled()) {
- kvm_s390_get_host_cpu_model(&max_model, &err);
+ if (!kvm_s390_get_host_cpu_model(&max_model, errp)) {
+ return NULL;
+ }
} else {
max_model.def = s390_find_cpu_def(QEMU_MAX_CPU_TYPE, QEMU_MAX_CPU_GEN,
QEMU_MAX_CPU_EC_GA, NULL);
bitmap_copy(max_model.features, qemu_max_cpu_feat, S390_FEAT_MAX);
}
- if (err) {
- error_propagate(errp, err);
- return NULL;
- }
cached = true;
return &max_model;
}
@@ -576,7 +575,6 @@ S390CPUModel *get_max_cpu_model(Error **errp)
void s390_realize_cpu_model(CPUState *cs, Error **errp)
{
ERRP_GUARD();
- Error *err = NULL;
S390CPUClass *xcc = S390_CPU_GET_CLASS(cs);
S390CPU *cpu = S390_CPU(cs);
const S390CPUModel *max_model;
@@ -605,9 +603,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp)
cpu->model->cpu_ver = max_model->cpu_ver;
check_consistency(cpu->model);
- check_compatibility(max_model, cpu->model, &err);
- if (err) {
- error_propagate(errp, err);
+ if (!check_compatibility(max_model, cpu->model, errp)) {
return;
}
diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h
index a89c2a15ab..71d4bc2dd4 100644
--- a/target/s390x/cpu_models.h
+++ b/target/s390x/cpu_models.h
@@ -115,7 +115,7 @@ S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga,
S390FeatBitmap features);
bool kvm_s390_cpu_models_supported(void);
-void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp);
-void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp);
+bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp);
+bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp);
#endif /* TARGET_S390X_CPU_MODELS_H */
diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c
index 2d99218069..977fbc6522 100644
--- a/target/s390x/cpu_models_sysemu.c
+++ b/target/s390x/cpu_models_sysemu.c
@@ -206,6 +206,14 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model,
} else {
info->props = QOBJECT(qdict);
}
+
+ /* features flagged as deprecated */
+ bitmap_zero(bitmap, S390_FEAT_MAX);
+ s390_get_deprecated_features(bitmap);
+
+ bitmap_and(bitmap, bitmap, model->def->full_feat, S390_FEAT_MAX);
+ s390_feat_bitmap_to_ascii(bitmap, &info->deprecated_props, list_add_feat);
+ info->has_deprecated_props = !!info->deprecated_props;
}
CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
@@ -389,7 +397,6 @@ CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa,
void apply_cpu_model(const S390CPUModel *model, Error **errp)
{
- Error *err = NULL;
static S390CPUModel applied_model;
static bool applied;
@@ -405,9 +412,7 @@ void apply_cpu_model(const S390CPUModel *model, Error **errp)
}
if (kvm_enabled()) {
- kvm_s390_apply_cpu_model(model, &err);
- if (err) {
- error_propagate(errp, err);
+ if (!kvm_s390_apply_cpu_model(model, errp)) {
return;
}
}
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 4dcd757cdc..1b494ecc20 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -2375,7 +2375,7 @@ bool kvm_s390_cpu_models_supported(void)
KVM_S390_VM_CPU_MACHINE_SUBFUNC);
}
-void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
+bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
{
struct kvm_s390_vm_cpu_machine prop = {};
struct kvm_device_attr attr = {
@@ -2390,14 +2390,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
if (!kvm_s390_cpu_models_supported()) {
error_setg(errp, "KVM doesn't support CPU models");
- return;
+ return false;
}
/* query the basic cpu model properties */
rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr);
if (rc) {
error_setg(errp, "KVM: Error querying host CPU model: %d", rc);
- return;
+ return false;
}
cpu_type = cpuid_type(prop.cpuid);
@@ -2420,13 +2420,13 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
rc = query_cpu_feat(model->features);
if (rc) {
error_setg(errp, "KVM: Error querying CPU features: %d", rc);
- return;
+ return false;
}
/* get supported cpu subfunctions indicated via query / test bit */
rc = query_cpu_subfunc(model->features);
if (rc) {
error_setg(errp, "KVM: Error querying CPU subfunctions: %d", rc);
- return;
+ return false;
}
/* PTFF subfunctions might be indicated although kernel support missing */
@@ -2482,7 +2482,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
}
if (!model->def) {
error_setg(errp, "KVM: host CPU model could not be identified");
- return;
+ return false;
}
/* for now, we can only provide the AP feature with HW support */
if (ap_available()) {
@@ -2506,6 +2506,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
/* strip of features that are not part of the maximum model */
bitmap_and(model->features, model->features, model->def->full_feat,
S390_FEAT_MAX);
+ return true;
}
static int configure_uv_feat_guest(const S390FeatBitmap features)
@@ -2542,7 +2543,7 @@ static void kvm_s390_configure_apie(bool interpret)
}
}
-void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
+bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
{
struct kvm_s390_vm_cpu_processor prop = {
.fac_list = { 0 },
@@ -2559,11 +2560,11 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
if (kvm_s390_cmma_available()) {
kvm_s390_enable_cmma();
}
- return;
+ return true;
}
if (!kvm_s390_cpu_models_supported()) {
error_setg(errp, "KVM doesn't support CPU models");
- return;
+ return false;
}
prop.cpuid = s390_cpuid_from_cpu_model(model);
prop.ibc = s390_ibc_from_cpu_model(model);
@@ -2573,19 +2574,19 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr);
if (rc) {
error_setg(errp, "KVM: Error configuring the CPU model: %d", rc);
- return;
+ return false;
}
/* configure cpu features indicated e.g. via SCLP */
rc = configure_cpu_feat(model->features);
if (rc) {
error_setg(errp, "KVM: Error configuring CPU features: %d", rc);
- return;
+ return false;
}
/* configure cpu subfunctions indicated via query / test bit */
rc = configure_cpu_subfunc(model->features);
if (rc) {
error_setg(errp, "KVM: Error configuring CPU subfunctions: %d", rc);
- return;
+ return false;
}
/* enable CMM via CMMA */
if (test_bit(S390_FEAT_CMM, model->features)) {
@@ -2600,8 +2601,9 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp)
rc = configure_uv_feat_guest(model->features);
if (rc) {
error_setg(errp, "KVM: Error configuring CPU UV features %d", rc);
- return;
+ return false;
}
+ return true;
}
void kvm_s390_restart_interrupt(S390CPU *cpu)
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index fbb2f1b4d4..f3a2f25a5c 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -24,6 +24,7 @@
#include "sysemu/kvm.h"
#include "sysemu/tcg.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "trace.h"
#include "hw/hw.h"
#include "hw/s390x/storage-keys.h"
diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c
index 9dd977349a..ad0ad61177 100644
--- a/target/s390x/sigp.c
+++ b/target/s390x/sigp.c
@@ -11,6 +11,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "s390x-internal.h"
+#include "hw/boards.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
#include "exec/address-spaces.h"
@@ -435,6 +436,22 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
return SIGP_CC_STATUS_STORED;
}
+S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
+{
+ static MachineState *ms;
+
+ if (!ms) {
+ ms = MACHINE(qdev_get_machine());
+ g_assert(ms->possible_cpus);
+ }
+
+ /* CPU address corresponds to the core_id and the index */
+ if (cpu_addr >= ms->possible_cpus->len) {
+ return NULL;
+ }
+ return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu);
+}
+
int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
{
uint64_t *status_reg = &env->regs[r1];
diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c
index 557831def4..6a308c5553 100644
--- a/target/s390x/tcg/mem_helper.c
+++ b/target/s390x/tcg/mem_helper.c
@@ -25,6 +25,7 @@
#include "tcg_s390x.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/cpu_ldst.h"
#include "hw/core/tcg-cpu-ops.h"
#include "qemu/int128.h"
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index 90a74ee795..ebd96abe6c 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -31,13 +31,11 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "s390x-internal.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
#include "qemu/log.h"
#include "qemu/host-utils.h"
-#include "exec/cpu_ldst.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
@@ -6192,6 +6190,8 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s)
const DisasInsn *info;
if (unlikely(s->ex_value)) {
+ uint64_t be_insn;
+
/* Drop the EX data now, so that it's clear on exception paths. */
tcg_gen_st_i64(tcg_constant_i64(0), tcg_env,
offsetof(CPUS390XState, ex_value));
@@ -6199,13 +6199,11 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s)
/* Extract the values saved by EXECUTE. */
insn = s->ex_value & 0xffffffffffff0000ull;
ilen = s->ex_value & 0xf;
+ op = insn >> 56;
/* Register insn bytes with translator so plugins work. */
- for (int i = 0; i < ilen; i++) {
- uint8_t byte = extract64(insn, 56 - (i * 8), 8);
- translator_fake_ldb(byte, pc + i);
- }
- op = insn >> 56;
+ be_insn = cpu_to_be64(insn);
+ translator_fake_ld(&s->base, &be_insn, get_ilen(op));
} else {
insn = ld_code2(env, s, pc);
op = (insn >> 8) & 0xff;
@@ -6472,7 +6470,7 @@ static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
static target_ulong get_next_pc(CPUS390XState *env, DisasContext *s,
uint64_t pc)
{
- uint64_t insn = cpu_lduw_code(env, pc);
+ uint64_t insn = translator_lduw(env, &s->base, pc);
return pc + get_ilen((insn >> 8) & 0xff);
}
@@ -6520,18 +6518,18 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void s390x_tr_disas_log(const DisasContextBase *dcbase,
+static bool s390x_tr_disas_log(const DisasContextBase *dcbase,
CPUState *cs, FILE *logfile)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
if (unlikely(dc->ex_value)) {
- /* ??? Unfortunately target_disas can't use host memory. */
- fprintf(logfile, "IN: EXECUTE %016" PRIx64, dc->ex_value);
- } else {
- fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first));
- target_disas(logfile, cs, dc->base.pc_first, dc->base.tb->size);
+ /* The ex_value has been recorded with translator_fake_ld. */
+ fprintf(logfile, "IN: EXECUTE\n");
+ target_disas(logfile, cs, &dc->base);
+ return true;
}
+ return false;
}
static const TranslatorOps s390x_tr_ops = {
diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig
index 2397c86028..93b92f1e48 100644
--- a/target/sh4/Kconfig
+++ b/target/sh4/Kconfig
@@ -1,2 +1,4 @@
config SH4
bool
+ # needed for sh_intc_get_pending_vector
+ select SH_INTC
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 43e35ec2ca..618aa7154e 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -47,7 +47,7 @@ static void superh_cpu_synchronize_from_tb(CPUState *cs,
{
SuperHCPU *cpu = SUPERH_CPU(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu->env.pc = tb->pc;
cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK;
}
@@ -74,7 +74,7 @@ static bool superh_io_recompile_replay_branch(CPUState *cs,
CPUSH4State *env = cpu_env(cs);
if ((env->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND))
- && !(cs->tcg_cflags & CF_PCREL) && env->pc != tb->pc) {
+ && !tcg_cflags_has(cs, CF_PCREL) && env->pc != tb->pc) {
env->pc -= 2;
env->flags &= ~(TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND);
return true;
diff --git a/target/sh4/helper.c b/target/sh4/helper.c
index 7c6f9d374a..6702910627 100644
--- a/target/sh4/helper.c
+++ b/target/sh4/helper.c
@@ -21,6 +21,7 @@
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/log.h"
#if !defined(CONFIG_USER_ONLY)
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index ebb6c901bf..53b092175d 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -19,7 +19,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "exec/helper-proto.h"
@@ -705,16 +704,20 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x300f: /* addv Rm,Rn */
{
- TCGv t0, t1, t2;
- t0 = tcg_temp_new();
- tcg_gen_add_i32(t0, REG(B7_4), REG(B11_8));
+ TCGv Rn = REG(B11_8);
+ TCGv Rm = REG(B7_4);
+ TCGv result, t1, t2;
+
+ result = tcg_temp_new();
t1 = tcg_temp_new();
- tcg_gen_xor_i32(t1, t0, REG(B11_8));
t2 = tcg_temp_new();
- tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8));
+ tcg_gen_add_i32(result, Rm, Rn);
+ /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */
+ tcg_gen_xor_i32(t1, result, Rn);
+ tcg_gen_xor_i32(t2, Rm, Rn);
tcg_gen_andc_i32(cpu_sr_t, t1, t2);
tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31);
- tcg_gen_mov_i32(REG(B7_4), t0);
+ tcg_gen_mov_i32(Rn, result);
}
return;
case 0x2009: /* and Rm,Rn */
@@ -929,16 +932,20 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x300b: /* subv Rm,Rn */
{
- TCGv t0, t1, t2;
- t0 = tcg_temp_new();
- tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4));
+ TCGv Rn = REG(B11_8);
+ TCGv Rm = REG(B7_4);
+ TCGv result, t1, t2;
+
+ result = tcg_temp_new();
t1 = tcg_temp_new();
- tcg_gen_xor_i32(t1, t0, REG(B7_4));
t2 = tcg_temp_new();
- tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4));
+ tcg_gen_sub_i32(result, Rn, Rm);
+ /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */
+ tcg_gen_xor_i32(t1, result, Rn);
+ tcg_gen_xor_i32(t2, Rn, Rm);
tcg_gen_and_i32(t1, t1, t2);
tcg_gen_shri_i32(cpu_sr_t, t1, 31);
- tcg_gen_mov_i32(REG(B11_8), t0);
+ tcg_gen_mov_i32(Rn, result);
}
return;
case 0x2008: /* tst Rm,Rn */
@@ -2181,6 +2188,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env)
*/
for (i = 1; i < max_insns; ++i) {
tcg_gen_insn_start(pc + i * 2, ctx->envflags);
+ ctx->base.insn_start = tcg_last_op();
}
}
#endif
@@ -2301,20 +2309,12 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void sh4_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cs, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps sh4_tr_ops = {
.init_disas_context = sh4_tr_init_disas_context,
.tb_start = sh4_tr_tb_start,
.insn_start = sh4_tr_insn_start,
.translate_insn = sh4_tr_translate_insn,
.tb_stop = sh4_tr_tb_stop,
- .disas_log = sh4_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index 485d416925..5be1592e66 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -206,7 +206,7 @@ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu)
static const sparc_def_t sparc_defs[] = {
#ifdef TARGET_SPARC64
{
- .name = "Fujitsu Sparc64",
+ .name = "Fujitsu-Sparc64",
.iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -215,7 +215,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Fujitsu Sparc64 III",
+ .name = "Fujitsu-Sparc64-III",
.iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -224,7 +224,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Fujitsu Sparc64 IV",
+ .name = "Fujitsu-Sparc64-IV",
.iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -233,7 +233,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Fujitsu Sparc64 V",
+ .name = "Fujitsu-Sparc64-V",
.iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -242,7 +242,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI UltraSparc I",
+ .name = "TI-UltraSparc-I",
.iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -251,7 +251,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI UltraSparc II",
+ .name = "TI-UltraSparc-II",
.iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -260,7 +260,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI UltraSparc IIi",
+ .name = "TI-UltraSparc-IIi",
.iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -269,7 +269,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI UltraSparc IIe",
+ .name = "TI-UltraSparc-IIe",
.iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -278,7 +278,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc III",
+ .name = "Sun-UltraSparc-III",
.iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -287,7 +287,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc III Cu",
+ .name = "Sun-UltraSparc-III-Cu",
.iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_3,
@@ -296,7 +296,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc IIIi",
+ .name = "Sun-UltraSparc-IIIi",
.iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -305,7 +305,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc IV",
+ .name = "Sun-UltraSparc-IV",
.iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_4,
@@ -314,7 +314,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc IV+",
+ .name = "Sun-UltraSparc-IV-plus",
.iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -323,7 +323,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT,
},
{
- .name = "Sun UltraSparc IIIi+",
+ .name = "Sun-UltraSparc-IIIi-plus",
.iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_3,
@@ -332,7 +332,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Sun UltraSparc T1",
+ .name = "Sun-UltraSparc-T1",
/* defined in sparc_ifu_fdp.v and ctu.h */
.iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)),
.fpu_version = 0x00000000,
@@ -343,7 +343,7 @@ static const sparc_def_t sparc_defs[] = {
| CPU_FEATURE_GL,
},
{
- .name = "Sun UltraSparc T2",
+ .name = "Sun-UltraSparc-T2",
/* defined in tlu_asi_ctl.v and n2_revid_cust.v */
.iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)),
.fpu_version = 0x00000000,
@@ -354,7 +354,7 @@ static const sparc_def_t sparc_defs[] = {
| CPU_FEATURE_GL,
},
{
- .name = "NEC UltraSparc I",
+ .name = "NEC-UltraSparc-I",
.iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
@@ -364,7 +364,7 @@ static const sparc_def_t sparc_defs[] = {
},
#else
{
- .name = "Fujitsu MB86904",
+ .name = "Fujitsu-MB86904",
.iu_version = 0x04 << 24, /* Impl 0, ver 4 */
.fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */
.mmu_version = 0x04 << 24, /* Impl 0, ver 4 */
@@ -377,7 +377,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "Fujitsu MB86907",
+ .name = "Fujitsu-MB86907",
.iu_version = 0x05 << 24, /* Impl 0, ver 5 */
.fpu_version = 4 << FSR_VER_SHIFT, /* FPU version 4 (Meiko) */
.mmu_version = 0x05 << 24, /* Impl 0, ver 5 */
@@ -390,7 +390,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI MicroSparc I",
+ .name = "TI-MicroSparc-I",
.iu_version = 0x41000000,
.fpu_version = 4 << FSR_VER_SHIFT,
.mmu_version = 0x41000000,
@@ -403,7 +403,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_FEATURE_MUL | CPU_FEATURE_DIV,
},
{
- .name = "TI MicroSparc II",
+ .name = "TI-MicroSparc-II",
.iu_version = 0x42000000,
.fpu_version = 4 << FSR_VER_SHIFT,
.mmu_version = 0x02000000,
@@ -416,7 +416,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI MicroSparc IIep",
+ .name = "TI-MicroSparc-IIep",
.iu_version = 0x42000000,
.fpu_version = 4 << FSR_VER_SHIFT,
.mmu_version = 0x04000000,
@@ -429,7 +429,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc 40", /* STP1020NPGA */
+ .name = "TI-SuperSparc-40", /* STP1020NPGA */
.iu_version = 0x41000000, /* SuperSPARC 2.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x00000800, /* SuperSPARC 2.x, no MXCC */
@@ -442,7 +442,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc 50", /* STP1020PGA */
+ .name = "TI-SuperSparc-50", /* STP1020PGA */
.iu_version = 0x40000000, /* SuperSPARC 3.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */
@@ -455,7 +455,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc 51",
+ .name = "TI-SuperSparc-51",
.iu_version = 0x40000000, /* SuperSPARC 3.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */
@@ -469,7 +469,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc 60", /* STP1020APGA */
+ .name = "TI-SuperSparc-60", /* STP1020APGA */
.iu_version = 0x40000000, /* SuperSPARC 3.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */
@@ -482,7 +482,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc 61",
+ .name = "TI-SuperSparc-61",
.iu_version = 0x44000000, /* SuperSPARC 3.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */
@@ -496,7 +496,7 @@ static const sparc_def_t sparc_defs[] = {
.features = CPU_DEFAULT_FEATURES,
},
{
- .name = "TI SuperSparc II",
+ .name = "TI-SuperSparc-II",
.iu_version = 0x40000000, /* SuperSPARC II 1.x */
.fpu_version = 0 << FSR_VER_SHIFT,
.mmu_version = 0x08000000, /* SuperSPARC II 1.x, MXCC */
@@ -702,7 +702,7 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs,
{
SPARCCPU *cpu = SPARC_CPU(cs);
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu->env.pc = tb->pc;
cpu->env.npc = tb->cs_base;
}
@@ -762,6 +762,16 @@ static ObjectClass *sparc_cpu_class_by_name(const char *cpu_model)
char *typename;
typename = sparc_cpu_type_name(cpu_model);
+
+ /* Fix up legacy names with '+' in it */
+ if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV+"))) {
+ g_free(typename);
+ typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV-plus"));
+ } else if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi+"))) {
+ g_free(typename);
+ typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi-plus"));
+ }
+
oc = object_class_by_name(typename);
g_free(typename);
return oc;
diff --git a/target/sparc/helper.h b/target/sparc/helper.h
index b8087d0d2b..97fbf6f66c 100644
--- a/target/sparc/helper.h
+++ b/target/sparc/helper.h
@@ -94,15 +94,12 @@ DEF_HELPER_FLAGS_2(fstox, TCG_CALL_NO_WG, s64, env, f32)
DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_WG, s64, env, f64)
DEF_HELPER_FLAGS_2(fqtox, TCG_CALL_NO_WG, s64, env, i128)
-DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i32, i32)
+DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64)
+DEF_HELPER_FLAGS_2(fmul8x16a, TCG_CALL_NO_RWG_SE, i64, i32, s32)
DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
-DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64)
+DEF_HELPER_FLAGS_1(fexpand, TCG_CALL_NO_RWG_SE, i64, i32)
DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64)
DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode
index 2d26404cb2..e2d8a07dc4 100644
--- a/target/sparc/insns.decode
+++ b/target/sparc/insns.decode
@@ -352,7 +352,7 @@ FCMPEq 10 000 cc:2 110101 rs1:5 0 0101 0111 rs2:5
FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @r_r_r
FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @r_r_r
BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @r_r_r
- FEXPAND 10 ..... 110110 ..... 0 0100 1101 ..... @r_r_r
+ FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @r_r2
FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @r_r1 # FSRC1d
FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index 2846a86cc4..7bdf99e0c0 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -23,6 +23,7 @@
#include "tcg/tcg.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "exec/cpu_ldst.h"
#include "asi.h"
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index ad1591d9fd..9ff06026b8 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -21,6 +21,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "qemu/qemu-print.h"
#include "trace.h"
diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 571b3e3f03..dca072888a 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
@@ -45,6 +44,7 @@
# define gen_helper_clear_softint(E, S) qemu_build_not_reached()
# define gen_helper_done(E) qemu_build_not_reached()
# define gen_helper_flushw(E) qemu_build_not_reached()
+# define gen_helper_fmul8x16a(D, S1, S2) qemu_build_not_reached()
# define gen_helper_rdccr(D, E) qemu_build_not_reached()
# define gen_helper_rdcwp(D, E) qemu_build_not_reached()
# define gen_helper_restored(E) qemu_build_not_reached()
@@ -72,11 +72,7 @@
# define gen_helper_fexpand ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fmul8sux16 ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fmul8ulx16 ({ qemu_build_not_reached(); NULL; })
-# define gen_helper_fmul8x16al ({ qemu_build_not_reached(); NULL; })
-# define gen_helper_fmul8x16au ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fmul8x16 ({ qemu_build_not_reached(); NULL; })
-# define gen_helper_fmuld8sux16 ({ qemu_build_not_reached(); NULL; })
-# define gen_helper_fmuld8ulx16 ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fpmerge ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fqtox ({ qemu_build_not_reached(); NULL; })
# define gen_helper_fstox ({ qemu_build_not_reached(); NULL; })
@@ -719,6 +715,60 @@ static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2)
#endif
}
+static void gen_op_fmul8x16al(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2)
+{
+ tcg_gen_ext16s_i32(src2, src2);
+ gen_helper_fmul8x16a(dst, src1, src2);
+}
+
+static void gen_op_fmul8x16au(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2)
+{
+ tcg_gen_sari_i32(src2, src2, 16);
+ gen_helper_fmul8x16a(dst, src1, src2);
+}
+
+static void gen_op_fmuld8ulx16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ TCGv_i32 t2 = tcg_temp_new_i32();
+
+ tcg_gen_ext8u_i32(t0, src1);
+ tcg_gen_ext16s_i32(t1, src2);
+ tcg_gen_mul_i32(t0, t0, t1);
+
+ tcg_gen_extract_i32(t1, src1, 16, 8);
+ tcg_gen_sextract_i32(t2, src2, 16, 16);
+ tcg_gen_mul_i32(t1, t1, t2);
+
+ tcg_gen_concat_i32_i64(dst, t0, t1);
+}
+
+static void gen_op_fmuld8sux16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ TCGv_i32 t2 = tcg_temp_new_i32();
+
+ /*
+ * The insn description talks about extracting the upper 8 bits
+ * of the signed 16-bit input rs1, performing the multiply, then
+ * shifting left by 8 bits. Instead, zap the lower 8 bits of
+ * the rs1 input, which avoids the need for two shifts.
+ */
+ tcg_gen_ext16s_i32(t0, src1);
+ tcg_gen_andi_i32(t0, t0, ~0xff);
+ tcg_gen_ext16s_i32(t1, src2);
+ tcg_gen_mul_i32(t0, t0, t1);
+
+ tcg_gen_sextract_i32(t1, src1, 16, 16);
+ tcg_gen_andi_i32(t1, t1, ~0xff);
+ tcg_gen_sextract_i32(t2, src2, 16, 16);
+ tcg_gen_mul_i32(t1, t1, t2);
+
+ tcg_gen_concat_i32_i64(dst, t0, t1);
+}
+
static void finishing_insn(DisasContext *dc)
{
/*
@@ -4358,6 +4408,25 @@ TRANS(FSQRTd, ALL, do_env_dd, a, gen_helper_fsqrtd)
TRANS(FxTOd, 64, do_env_dd, a, gen_helper_fxtod)
TRANS(FdTOx, 64, do_env_dd, a, gen_helper_fdtox)
+static bool do_df(DisasContext *dc, arg_r_r *a,
+ void (*func)(TCGv_i64, TCGv_i32))
+{
+ TCGv_i64 dst;
+ TCGv_i32 src;
+
+ if (gen_trap_ifnofpu(dc)) {
+ return true;
+ }
+
+ dst = tcg_temp_new_i64();
+ src = gen_load_fpr_F(dc, a->rs);
+ func(dst, src);
+ gen_store_fpr_D(dc, a->rd, dst);
+ return advance_pc(dc);
+}
+
+TRANS(FEXPAND, VIS1, do_df, a, gen_helper_fexpand)
+
static bool do_env_df(DisasContext *dc, arg_r_r *a,
void (*func)(TCGv_i64, TCGv_env, TCGv_i32))
{
@@ -4564,6 +4633,50 @@ TRANS(FSUBs, ALL, do_env_fff, a, gen_helper_fsubs)
TRANS(FMULs, ALL, do_env_fff, a, gen_helper_fmuls)
TRANS(FDIVs, ALL, do_env_fff, a, gen_helper_fdivs)
+static bool do_dff(DisasContext *dc, arg_r_r_r *a,
+ void (*func)(TCGv_i64, TCGv_i32, TCGv_i32))
+{
+ TCGv_i64 dst;
+ TCGv_i32 src1, src2;
+
+ if (gen_trap_ifnofpu(dc)) {
+ return true;
+ }
+
+ dst = gen_dest_fpr_D(dc, a->rd);
+ src1 = gen_load_fpr_F(dc, a->rs1);
+ src2 = gen_load_fpr_F(dc, a->rs2);
+ func(dst, src1, src2);
+ gen_store_fpr_D(dc, a->rd, dst);
+ return advance_pc(dc);
+}
+
+TRANS(FMUL8x16AU, VIS1, do_dff, a, gen_op_fmul8x16au)
+TRANS(FMUL8x16AL, VIS1, do_dff, a, gen_op_fmul8x16al)
+TRANS(FMULD8SUx16, VIS1, do_dff, a, gen_op_fmuld8sux16)
+TRANS(FMULD8ULx16, VIS1, do_dff, a, gen_op_fmuld8ulx16)
+TRANS(FPMERGE, VIS1, do_dff, a, gen_helper_fpmerge)
+
+static bool do_dfd(DisasContext *dc, arg_r_r_r *a,
+ void (*func)(TCGv_i64, TCGv_i32, TCGv_i64))
+{
+ TCGv_i64 dst, src2;
+ TCGv_i32 src1;
+
+ if (gen_trap_ifnofpu(dc)) {
+ return true;
+ }
+
+ dst = gen_dest_fpr_D(dc, a->rd);
+ src1 = gen_load_fpr_F(dc, a->rs1);
+ src2 = gen_load_fpr_D(dc, a->rs2);
+ func(dst, src1, src2);
+ gen_store_fpr_D(dc, a->rd, dst);
+ return advance_pc(dc);
+}
+
+TRANS(FMUL8x16, VIS1, do_dfd, a, gen_helper_fmul8x16)
+
static bool do_ddd(DisasContext *dc, arg_r_r_r *a,
void (*func)(TCGv_i64, TCGv_i64, TCGv_i64))
{
@@ -4581,15 +4694,8 @@ static bool do_ddd(DisasContext *dc, arg_r_r_r *a,
return advance_pc(dc);
}
-TRANS(FMUL8x16, VIS1, do_ddd, a, gen_helper_fmul8x16)
-TRANS(FMUL8x16AU, VIS1, do_ddd, a, gen_helper_fmul8x16au)
-TRANS(FMUL8x16AL, VIS1, do_ddd, a, gen_helper_fmul8x16al)
TRANS(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16)
TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16)
-TRANS(FMULD8SUx16, VIS1, do_ddd, a, gen_helper_fmuld8sux16)
-TRANS(FMULD8ULx16, VIS1, do_ddd, a, gen_helper_fmuld8ulx16)
-TRANS(FPMERGE, VIS1, do_ddd, a, gen_helper_fpmerge)
-TRANS(FEXPAND, VIS1, do_ddd, a, gen_helper_fexpand)
TRANS(FPADD16, VIS1, do_ddd, a, tcg_gen_vec_add16_i64)
TRANS(FPADD32, VIS1, do_ddd, a, tcg_gen_vec_add32_i64)
@@ -5042,20 +5148,12 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
}
}
-static void sparc_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps sparc_tr_ops = {
.init_disas_context = sparc_tr_init_disas_context,
.tb_start = sparc_tr_tb_start,
.insn_start = sparc_tr_insn_start,
.translate_insn = sparc_tr_translate_insn,
.tb_stop = sparc_tr_tb_stop,
- .disas_log = sparc_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
diff --git a/target/sparc/vis_helper.c b/target/sparc/vis_helper.c
index 7763b16c24..e15c6bb34e 100644
--- a/target/sparc/vis_helper.c
+++ b/target/sparc/vis_helper.c
@@ -44,6 +44,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
#if HOST_BIG_ENDIAN
#define VIS_B64(n) b[7 - (n)]
+#define VIS_SB64(n) sb[7 - (n)]
#define VIS_W64(n) w[3 - (n)]
#define VIS_SW64(n) sw[3 - (n)]
#define VIS_L64(n) l[1 - (n)]
@@ -51,6 +52,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
#define VIS_W32(n) w[1 - (n)]
#else
#define VIS_B64(n) b[n]
+#define VIS_SB64(n) sb[n]
#define VIS_W64(n) w[n]
#define VIS_SW64(n) sw[n]
#define VIS_L64(n) l[n]
@@ -60,6 +62,7 @@ target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
typedef union {
uint8_t b[8];
+ int8_t sb[8];
uint16_t w[4];
int16_t sw[4];
uint32_t l[2];
@@ -74,94 +77,60 @@ typedef union {
float32 f;
} VIS32;
-uint64_t helper_fpmerge(uint64_t src1, uint64_t src2)
+uint64_t helper_fpmerge(uint32_t src1, uint32_t src2)
{
- VIS64 s, d;
+ VIS32 s1, s2;
+ VIS64 d;
- s.ll = src1;
- d.ll = src2;
+ s1.l = src1;
+ s2.l = src2;
+ d.ll = 0;
- /* Reverse calculation order to handle overlap */
- d.VIS_B64(7) = s.VIS_B64(3);
- d.VIS_B64(6) = d.VIS_B64(3);
- d.VIS_B64(5) = s.VIS_B64(2);
- d.VIS_B64(4) = d.VIS_B64(2);
- d.VIS_B64(3) = s.VIS_B64(1);
- d.VIS_B64(2) = d.VIS_B64(1);
- d.VIS_B64(1) = s.VIS_B64(0);
- /* d.VIS_B64(0) = d.VIS_B64(0); */
+ d.VIS_B64(7) = s1.VIS_B32(3);
+ d.VIS_B64(6) = s2.VIS_B32(3);
+ d.VIS_B64(5) = s1.VIS_B32(2);
+ d.VIS_B64(4) = s2.VIS_B32(2);
+ d.VIS_B64(3) = s1.VIS_B32(1);
+ d.VIS_B64(2) = s2.VIS_B32(1);
+ d.VIS_B64(1) = s1.VIS_B32(0);
+ d.VIS_B64(0) = s2.VIS_B32(0);
return d.ll;
}
-uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2)
+static inline int do_ms16b(int x, int y)
{
- VIS64 s, d;
- uint32_t tmp;
-
- s.ll = src1;
- d.ll = src2;
-
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_W64(r) = tmp >> 8;
-
- PMUL(0);
- PMUL(1);
- PMUL(2);
- PMUL(3);
-#undef PMUL
-
- return d.ll;
+ return ((x * y) + 0x80) >> 8;
}
-uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2)
+uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2)
{
- VIS64 s, d;
- uint32_t tmp;
+ VIS64 d;
+ VIS32 s;
- s.ll = src1;
+ s.l = src1;
d.ll = src2;
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_W64(r) = tmp >> 8;
-
- PMUL(0);
- PMUL(1);
- PMUL(2);
- PMUL(3);
-#undef PMUL
+ d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), d.VIS_SW64(0));
+ d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), d.VIS_SW64(1));
+ d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), d.VIS_SW64(2));
+ d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), d.VIS_SW64(3));
return d.ll;
}
-uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2)
+uint64_t helper_fmul8x16a(uint32_t src1, int32_t src2)
{
- VIS64 s, d;
- uint32_t tmp;
-
- s.ll = src1;
- d.ll = src2;
+ VIS32 s;
+ VIS64 d;
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_W64(r) = tmp >> 8;
+ s.l = src1;
+ d.ll = 0;
- PMUL(0);
- PMUL(1);
- PMUL(2);
- PMUL(3);
-#undef PMUL
+ d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), src2);
+ d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), src2);
+ d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), src2);
+ d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), src2);
return d.ll;
}
@@ -169,23 +138,14 @@ uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2)
uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
- uint32_t tmp;
s.ll = src1;
d.ll = src2;
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_W64(r) = tmp >> 8;
-
- PMUL(0);
- PMUL(1);
- PMUL(2);
- PMUL(3);
-#undef PMUL
+ d.VIS_W64(0) = do_ms16b(s.VIS_SB64(1), d.VIS_SW64(0));
+ d.VIS_W64(1) = do_ms16b(s.VIS_SB64(3), d.VIS_SW64(1));
+ d.VIS_W64(2) = do_ms16b(s.VIS_SB64(5), d.VIS_SW64(2));
+ d.VIS_W64(3) = do_ms16b(s.VIS_SB64(7), d.VIS_SW64(3));
return d.ll;
}
@@ -193,80 +153,25 @@ uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2)
uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
- uint32_t tmp;
s.ll = src1;
d.ll = src2;
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_W64(r) = tmp >> 8;
-
- PMUL(0);
- PMUL(1);
- PMUL(2);
- PMUL(3);
-#undef PMUL
-
- return d.ll;
-}
-
-uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2)
-{
- VIS64 s, d;
- uint32_t tmp;
-
- s.ll = src1;
- d.ll = src2;
-
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_L64(r) = tmp;
-
- /* Reverse calculation order to handle overlap */
- PMUL(1);
- PMUL(0);
-#undef PMUL
+ d.VIS_W64(0) = do_ms16b(s.VIS_B64(0), d.VIS_SW64(0));
+ d.VIS_W64(1) = do_ms16b(s.VIS_B64(2), d.VIS_SW64(1));
+ d.VIS_W64(2) = do_ms16b(s.VIS_B64(4), d.VIS_SW64(2));
+ d.VIS_W64(3) = do_ms16b(s.VIS_B64(6), d.VIS_SW64(3));
return d.ll;
}
-uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2)
-{
- VIS64 s, d;
- uint32_t tmp;
-
- s.ll = src1;
- d.ll = src2;
-
-#define PMUL(r) \
- tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
- if ((tmp & 0xff) > 0x7f) { \
- tmp += 0x100; \
- } \
- d.VIS_L64(r) = tmp;
-
- /* Reverse calculation order to handle overlap */
- PMUL(1);
- PMUL(0);
-#undef PMUL
-
- return d.ll;
-}
-
-uint64_t helper_fexpand(uint64_t src1, uint64_t src2)
+uint64_t helper_fexpand(uint32_t src2)
{
VIS32 s;
VIS64 d;
- s.l = (uint32_t)src1;
- d.ll = src2;
+ s.l = src2;
+ d.ll = 0;
d.VIS_W64(0) = s.VIS_B32(0) << 4;
d.VIS_W64(1) = s.VIS_B32(1) << 4;
d.VIS_W64(2) = s.VIS_B32(2) << 4;
diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c
index 8f9b72c3a0..bdefb84511 100644
--- a/target/tricore/cpu.c
+++ b/target/tricore/cpu.c
@@ -47,7 +47,7 @@ static vaddr tricore_cpu_get_pc(CPUState *cs)
static void tricore_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+ tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
cpu_env(cs)->PC = tb->pc;
}
diff --git a/target/tricore/helper.c b/target/tricore/helper.c
index 76bd226370..7014255f77 100644
--- a/target/tricore/helper.c
+++ b/target/tricore/helper.c
@@ -20,6 +20,7 @@
#include "hw/registerfields.h"
#include "cpu.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#include "fpu/softfloat-helpers.h"
#include "qemu/qemu-print.h"
diff --git a/target/tricore/translate.c b/target/tricore/translate.c
index c45e1d992e..a46a03e1fd 100644
--- a/target/tricore/translate.c
+++ b/target/tricore/translate.c
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "exec/cpu_ldst.h"
@@ -8453,20 +8452,12 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void tricore_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps tricore_tr_ops = {
.init_disas_context = tricore_tr_init_disas_context,
.tb_start = tricore_tr_tb_start,
.insn_start = tricore_tr_insn_start,
.translate_insn = tricore_tr_translate_insn,
.tb_stop = tricore_tr_tb_stop,
- .disas_log = tricore_tr_disas_log,
};
diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c
index 47063b0a57..997b21d389 100644
--- a/target/xtensa/mmu_helper.c
+++ b/target/xtensa/mmu_helper.c
@@ -33,6 +33,7 @@
#include "exec/helper-proto.h"
#include "qemu/host-utils.h"
#include "exec/exec-all.h"
+#include "exec/page-protection.h"
#define XTENSA_MPU_SEGMENT_MASK 0x0000001f
#define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00
diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c
index 496754ba57..028d4e0a1c 100644
--- a/target/xtensa/op_helper.c
+++ b/target/xtensa/op_helper.c
@@ -28,6 +28,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/helper-proto.h"
+#include "exec/page-protection.h"
#include "qemu/host-utils.h"
#include "exec/exec-all.h"
#include "qemu/atomic.h"
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index b206d57fc4..75b7bfda4c 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -32,11 +32,9 @@
#include "cpu.h"
#include "exec/exec-all.h"
-#include "disas/disas.h"
#include "tcg/tcg-op.h"
#include "qemu/log.h"
#include "qemu/qemu-print.h"
-#include "exec/cpu_ldst.h"
#include "semihosting/semihost.h"
#include "exec/translator.h"
@@ -1119,7 +1117,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc)
{
- uint8_t b0 = cpu_ldub_code(env, dc->pc);
+ uint8_t b0 = translator_ldub(env, &dc->base, dc->pc);
return xtensa_op0_insn_len(dc, b0);
}
@@ -1221,20 +1219,12 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
}
}
-static void xtensa_tr_disas_log(const DisasContextBase *dcbase,
- CPUState *cpu, FILE *logfile)
-{
- fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first));
- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
-}
-
static const TranslatorOps xtensa_translator_ops = {
.init_disas_context = xtensa_tr_init_disas_context,
.tb_start = xtensa_tr_tb_start,
.insn_start = xtensa_tr_insn_start,
.translate_insn = xtensa_tr_translate_insn,
.tb_stop = xtensa_tr_tb_stop,
- .disas_log = xtensa_tr_disas_log,
};
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns,
diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc
index c6ba498623..59235b4f38 100644
--- a/tcg/i386/tcg-target.c.inc
+++ b/tcg/i386/tcg-target.c.inc
@@ -1658,6 +1658,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond,
TCGArg dest, TCGArg arg1, TCGArg arg2,
int const_arg2, bool neg)
{
+ int cmp_rexw = rexw;
bool inv = false;
bool cleared;
int jcc;
@@ -1674,6 +1675,18 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond,
}
break;
+ case TCG_COND_TSTNE:
+ inv = true;
+ /* fall through */
+ case TCG_COND_TSTEQ:
+ /* If arg2 is -1, convert to LTU/GEU vs 1. */
+ if (const_arg2 && arg2 == 0xffffffffu) {
+ arg2 = 1;
+ cmp_rexw = 0;
+ goto do_ltu;
+ }
+ break;
+
case TCG_COND_LEU:
inv = true;
/* fall through */
@@ -1697,7 +1710,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond,
* We can then use NEG or INC to produce the desired result.
* This is always smaller than the SETCC expansion.
*/
- tcg_out_cmp(s, TCG_COND_LTU, arg1, arg2, const_arg2, rexw);
+ tcg_out_cmp(s, TCG_COND_LTU, arg1, arg2, const_arg2, cmp_rexw);
/* X - X - C = -C = (C ? -1 : 0) */
tgen_arithr(s, ARITH_SBB + (neg ? rexw : 0), dest, dest);
@@ -1744,7 +1757,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond,
cleared = true;
}
- jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, rexw);
+ jcc = tcg_out_cmp(s, cond, arg1, arg2, const_arg2, cmp_rexw);
tcg_out_modrm(s, OPC_SETCC | jcc, 0, dest);
if (!cleared) {
@@ -3769,49 +3782,20 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece)
}
}
-static void expand_vec_shi(TCGType type, unsigned vece, TCGOpcode opc,
+static void expand_vec_shi(TCGType type, unsigned vece, bool right,
TCGv_vec v0, TCGv_vec v1, TCGArg imm)
{
- TCGv_vec t1, t2;
+ uint8_t mask;
tcg_debug_assert(vece == MO_8);
-
- t1 = tcg_temp_new_vec(type);
- t2 = tcg_temp_new_vec(type);
-
- /*
- * Unpack to W, shift, and repack. Tricky bits:
- * (1) Use punpck*bw x,x to produce DDCCBBAA,
- * i.e. duplicate in other half of the 16-bit lane.
- * (2) For right-shift, add 8 so that the high half of the lane
- * becomes zero. For left-shift, and left-rotate, we must
- * shift up and down again.
- * (3) Step 2 leaves high half zero such that PACKUSWB
- * (pack with unsigned saturation) does not modify
- * the quantity.
- */
- vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8,
- tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(v1));
- vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8,
- tcgv_vec_arg(t2), tcgv_vec_arg(v1), tcgv_vec_arg(v1));
-
- if (opc != INDEX_op_rotli_vec) {
- imm += 8;
- }
- if (opc == INDEX_op_shri_vec) {
- tcg_gen_shri_vec(MO_16, t1, t1, imm);
- tcg_gen_shri_vec(MO_16, t2, t2, imm);
+ if (right) {
+ mask = 0xff >> imm;
+ tcg_gen_shri_vec(MO_16, v0, v1, imm);
} else {
- tcg_gen_shli_vec(MO_16, t1, t1, imm);
- tcg_gen_shli_vec(MO_16, t2, t2, imm);
- tcg_gen_shri_vec(MO_16, t1, t1, 8);
- tcg_gen_shri_vec(MO_16, t2, t2, 8);
+ mask = 0xff << imm;
+ tcg_gen_shli_vec(MO_16, v0, v1, imm);
}
-
- vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8,
- tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t2));
- tcg_temp_free_vec(t1);
- tcg_temp_free_vec(t2);
+ tcg_gen_and_vec(MO_8, v0, v0, tcg_constant_vec(type, MO_8, mask));
}
static void expand_vec_sari(TCGType type, unsigned vece,
@@ -3821,7 +3805,7 @@ static void expand_vec_sari(TCGType type, unsigned vece,
switch (vece) {
case MO_8:
- /* Unpack to W, shift, and repack, as in expand_vec_shi. */
+ /* Unpack to 16-bit, shift, and repack. */
t1 = tcg_temp_new_vec(type);
t2 = tcg_temp_new_vec(type);
vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8,
@@ -3874,12 +3858,7 @@ static void expand_vec_rotli(TCGType type, unsigned vece,
{
TCGv_vec t;
- if (vece == MO_8) {
- expand_vec_shi(type, vece, INDEX_op_rotli_vec, v0, v1, imm);
- return;
- }
-
- if (have_avx512vbmi2) {
+ if (vece != MO_8 && have_avx512vbmi2) {
vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece,
tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm);
return;
@@ -4155,10 +4134,11 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece,
switch (opc) {
case INDEX_op_shli_vec:
+ expand_vec_shi(type, vece, false, v0, v1, a2);
+ break;
case INDEX_op_shri_vec:
- expand_vec_shi(type, vece, opc, v0, v1, a2);
+ expand_vec_shi(type, vece, true, v0, v1, a2);
break;
-
case INDEX_op_sari_vec:
expand_vec_sari(type, vece, v0, v1, a2);
break;
diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc
index 69c5b8ac4f..06ca1ab11c 100644
--- a/tcg/loongarch64/tcg-target.c.inc
+++ b/tcg/loongarch64/tcg-target.c.inc
@@ -808,18 +808,88 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data,
}
}
-static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg,
- TCGReg arg1, intptr_t arg2)
+static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest,
+ TCGReg base, intptr_t offset)
{
- bool is_32bit = type == TCG_TYPE_I32;
- tcg_out_ldst(s, is_32bit ? OPC_LD_W : OPC_LD_D, arg, arg1, arg2);
+ switch (type) {
+ case TCG_TYPE_I32:
+ if (dest < TCG_REG_V0) {
+ tcg_out_ldst(s, OPC_LD_W, dest, base, offset);
+ } else {
+ tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_32, dest, base, offset);
+ }
+ break;
+ case TCG_TYPE_I64:
+ if (dest < TCG_REG_V0) {
+ tcg_out_ldst(s, OPC_LD_D, dest, base, offset);
+ } else {
+ tcg_out_dupm_vec(s, TCG_TYPE_I128, MO_64, dest, base, offset);
+ }
+ break;
+ case TCG_TYPE_V128:
+ if (-0x800 <= offset && offset <= 0x7ff) {
+ tcg_out_opc_vld(s, dest, base, offset);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset);
+ tcg_out_opc_vldx(s, dest, base, TCG_REG_TMP0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
-static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg,
- TCGReg arg1, intptr_t arg2)
+static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src,
+ TCGReg base, intptr_t offset)
{
- bool is_32bit = type == TCG_TYPE_I32;
- tcg_out_ldst(s, is_32bit ? OPC_ST_W : OPC_ST_D, arg, arg1, arg2);
+ switch (type) {
+ case TCG_TYPE_I32:
+ if (src < TCG_REG_V0) {
+ tcg_out_ldst(s, OPC_ST_W, src, base, offset);
+ } else {
+ /* TODO: Could use fst_s, fstx_s */
+ if (offset < -0x100 || offset > 0xff || (offset & 3)) {
+ if (-0x800 <= offset && offset <= 0x7ff) {
+ tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset);
+ tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base);
+ }
+ base = TCG_REG_TMP0;
+ offset = 0;
+ }
+ tcg_out_opc_vstelm_w(s, src, base, offset, 0);
+ }
+ break;
+ case TCG_TYPE_I64:
+ if (src < TCG_REG_V0) {
+ tcg_out_ldst(s, OPC_ST_D, src, base, offset);
+ } else {
+ /* TODO: Could use fst_d, fstx_d */
+ if (offset < -0x100 || offset > 0xff || (offset & 7)) {
+ if (-0x800 <= offset && offset <= 0x7ff) {
+ tcg_out_opc_addi_d(s, TCG_REG_TMP0, base, offset);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset);
+ tcg_out_opc_add_d(s, TCG_REG_TMP0, TCG_REG_TMP0, base);
+ }
+ base = TCG_REG_TMP0;
+ offset = 0;
+ }
+ tcg_out_opc_vstelm_d(s, src, base, offset, 0);
+ }
+ break;
+ case TCG_TYPE_V128:
+ if (-0x800 <= offset && offset <= 0x7ff) {
+ tcg_out_opc_vst(s, src, base, offset);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset);
+ tcg_out_opc_vstx(s, src, base, TCG_REG_TMP0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val,
@@ -1740,7 +1810,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc,
{
TCGType type = vecl + TCG_TYPE_V64;
TCGArg a0, a1, a2, a3;
- TCGReg temp = TCG_REG_TMP0;
TCGReg temp_vec = TCG_VEC_TMP0;
static const LoongArchInsn cmp_vec_insn[16][4] = {
@@ -1820,22 +1889,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc,
switch (opc) {
case INDEX_op_st_vec:
- /* Try to fit vst imm */
- if (-0x800 <= a2 && a2 <= 0x7ff) {
- tcg_out_opc_vst(s, a0, a1, a2);
- } else {
- tcg_out_movi(s, TCG_TYPE_I64, temp, a2);
- tcg_out_opc_vstx(s, a0, a1, temp);
- }
+ tcg_out_st(s, type, a0, a1, a2);
break;
case INDEX_op_ld_vec:
- /* Try to fit vld imm */
- if (-0x800 <= a2 && a2 <= 0x7ff) {
- tcg_out_opc_vld(s, a0, a1, a2);
- } else {
- tcg_out_movi(s, TCG_TYPE_I64, temp, a2);
- tcg_out_opc_vldx(s, a0, a1, temp);
- }
+ tcg_out_ld(s, type, a0, a1, a2);
break;
case INDEX_op_and_vec:
tcg_out_opc_vand_v(s, a0, a1, a2);
diff --git a/tcg/optimize.c b/tcg/optimize.c
index 2e9e5725a9..8886f7037a 100644
--- a/tcg/optimize.c
+++ b/tcg/optimize.c
@@ -2099,6 +2099,108 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op)
return false;
}
+static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg)
+{
+ uint64_t a_zmask, b_val;
+ TCGCond cond;
+
+ if (!arg_is_const(op->args[2])) {
+ return false;
+ }
+
+ a_zmask = arg_info(op->args[1])->z_mask;
+ b_val = arg_info(op->args[2])->val;
+ cond = op->args[3];
+
+ if (ctx->type == TCG_TYPE_I32) {
+ a_zmask = (uint32_t)a_zmask;
+ b_val = (uint32_t)b_val;
+ }
+
+ /*
+ * A with only low bits set vs B with high bits set means that A < B.
+ */
+ if (a_zmask < b_val) {
+ bool inv = false;
+
+ switch (cond) {
+ case TCG_COND_NE:
+ case TCG_COND_LEU:
+ case TCG_COND_LTU:
+ inv = true;
+ /* fall through */
+ case TCG_COND_GTU:
+ case TCG_COND_GEU:
+ case TCG_COND_EQ:
+ return tcg_opt_gen_movi(ctx, op, op->args[0], neg ? -inv : inv);
+ default:
+ break;
+ }
+ }
+
+ /*
+ * A with only lsb set is already boolean.
+ */
+ if (a_zmask <= 1) {
+ bool convert = false;
+ bool inv = false;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ inv = true;
+ /* fall through */
+ case TCG_COND_NE:
+ convert = (b_val == 0);
+ break;
+ case TCG_COND_LTU:
+ case TCG_COND_TSTEQ:
+ inv = true;
+ /* fall through */
+ case TCG_COND_GEU:
+ case TCG_COND_TSTNE:
+ convert = (b_val == 1);
+ break;
+ default:
+ break;
+ }
+ if (convert) {
+ TCGOpcode add_opc, xor_opc, neg_opc;
+
+ if (!inv && !neg) {
+ return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]);
+ }
+
+ switch (ctx->type) {
+ case TCG_TYPE_I32:
+ add_opc = INDEX_op_add_i32;
+ neg_opc = INDEX_op_neg_i32;
+ xor_opc = INDEX_op_xor_i32;
+ break;
+ case TCG_TYPE_I64:
+ add_opc = INDEX_op_add_i64;
+ neg_opc = INDEX_op_neg_i64;
+ xor_opc = INDEX_op_xor_i64;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (!inv) {
+ op->opc = neg_opc;
+ } else if (neg) {
+ op->opc = add_opc;
+ op->args[2] = arg_new_constant(ctx, -1);
+ } else {
+ op->opc = xor_opc;
+ op->args[2] = arg_new_constant(ctx, 1);
+ }
+ return false;
+ }
+ }
+
+ return false;
+}
+
static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg)
{
TCGOpcode and_opc, sub_opc, xor_opc, neg_opc, shr_opc;
@@ -2200,6 +2302,10 @@ static bool fold_setcond(OptContext *ctx, TCGOp *op)
if (i >= 0) {
return tcg_opt_gen_movi(ctx, op, op->args[0], i);
}
+
+ if (fold_setcond_zmask(ctx, op, false)) {
+ return true;
+ }
fold_setcond_tst_pow2(ctx, op, false);
ctx->z_mask = 1;
@@ -2214,6 +2320,10 @@ static bool fold_negsetcond(OptContext *ctx, TCGOp *op)
if (i >= 0) {
return tcg_opt_gen_movi(ctx, op, op->args[0], -i);
}
+
+ if (fold_setcond_zmask(ctx, op, true)) {
+ return true;
+ }
fold_setcond_tst_pow2(ctx, op, true);
/* Value is {0,-1} so all bits are repetitions of the sign. */
diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c
index bb88943f79..0308732d9b 100644
--- a/tcg/tcg-op-gvec.c
+++ b/tcg/tcg-op-gvec.c
@@ -785,7 +785,8 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs,
}
static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs,
- uint32_t oprsz, int32_t c, bool load_dest,
+ uint32_t oprsz, int32_t c,
+ bool load_dest, bool write_aofs,
void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t))
{
TCGv_i32 t0 = tcg_temp_new_i32();
@@ -801,6 +802,9 @@ static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs,
}
fni(t2, t0, t1, c);
tcg_gen_st_i32(t2, tcg_env, dofs + i);
+ if (write_aofs) {
+ tcg_gen_st_i32(t0, tcg_env, aofs + i);
+ }
}
tcg_temp_free_i32(t0);
tcg_temp_free_i32(t1);
@@ -944,7 +948,8 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs,
}
static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs,
- uint32_t oprsz, int64_t c, bool load_dest,
+ uint32_t oprsz, int64_t c,
+ bool load_dest, bool write_aofs,
void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t))
{
TCGv_i64 t0 = tcg_temp_new_i64();
@@ -960,6 +965,9 @@ static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs,
}
fni(t2, t0, t1, c);
tcg_gen_st_i64(t2, tcg_env, dofs + i);
+ if (write_aofs) {
+ tcg_gen_st_i64(t0, tcg_env, aofs + i);
+ }
}
tcg_temp_free_i64(t0);
tcg_temp_free_i64(t1);
@@ -1102,7 +1110,8 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs,
*/
static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs,
uint32_t bofs, uint32_t oprsz, uint32_t tysz,
- TCGType type, int64_t c, bool load_dest,
+ TCGType type, int64_t c,
+ bool load_dest, bool write_aofs,
void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec,
int64_t))
{
@@ -1118,6 +1127,9 @@ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs,
}
fni(vece, t2, t0, t1, c);
tcg_gen_st_vec(t2, tcg_env, dofs + i);
+ if (write_aofs) {
+ tcg_gen_st_vec(t0, tcg_env, aofs + i);
+ }
}
}
@@ -1471,7 +1483,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs,
*/
some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_3i_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256,
- c, g->load_dest, g->fniv);
+ c, g->load_dest, g->write_aofs, g->fniv);
if (some == oprsz) {
break;
}
@@ -1483,18 +1495,20 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs,
/* fallthru */
case TCG_TYPE_V128:
expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128,
- c, g->load_dest, g->fniv);
+ c, g->load_dest, g->write_aofs, g->fniv);
break;
case TCG_TYPE_V64:
expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64,
- c, g->load_dest, g->fniv);
+ c, g->load_dest, g->write_aofs, g->fniv);
break;
case 0:
if (g->fni8 && check_size_impl(oprsz, 8)) {
- expand_3i_i64(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni8);
+ expand_3i_i64(dofs, aofs, bofs, oprsz, c,
+ g->load_dest, g->write_aofs, g->fni8);
} else if (g->fni4 && check_size_impl(oprsz, 4)) {
- expand_3i_i32(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni4);
+ expand_3i_i32(dofs, aofs, bofs, oprsz, c,
+ g->load_dest, g->write_aofs, g->fni4);
} else {
assert(g->fno != NULL);
tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, c, g->fno);
diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c
index f11043b449..8510160258 100644
--- a/tcg/tcg-op-ldst.c
+++ b/tcg/tcg-op-ldst.c
@@ -161,14 +161,14 @@ plugin_gen_mem_callbacks(TCGv_i64 copy_addr, TCGTemp *orig_addr, MemOpIdx oi,
copy_addr = tcg_temp_ebb_new_i64();
tcg_gen_extu_i32_i64(copy_addr, temp_tcgv_i32(orig_addr));
}
- plugin_gen_empty_mem_callback(copy_addr, info);
+ tcg_gen_plugin_mem_cb(copy_addr, info);
tcg_temp_free_i64(copy_addr);
} else {
if (copy_addr) {
- plugin_gen_empty_mem_callback(copy_addr, info);
+ tcg_gen_plugin_mem_cb(copy_addr, info);
tcg_temp_free_i64(copy_addr);
} else {
- plugin_gen_empty_mem_callback(temp_tcgv_i64(orig_addr), info);
+ tcg_gen_plugin_mem_cb(temp_tcgv_i64(orig_addr), info);
}
}
}
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index aa6bc6f57d..eff3728622 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -312,14 +312,14 @@ void tcg_gen_mb(TCGBar mb_type)
}
}
-void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr)
+void tcg_gen_plugin_cb(unsigned from)
{
- tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr);
+ tcg_gen_op1(INDEX_op_plugin_cb, from);
}
-void tcg_gen_plugin_cb_end(void)
+void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo)
{
- tcg_emit_op(INDEX_op_plugin_cb_end, 0);
+ tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo);
}
/* 32 bit ops */
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 6a32656cd4..34e3056380 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -761,15 +761,6 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) -
< MIN_TLB_MASK_TABLE_OFS);
#endif
-static void alloc_tcg_plugin_context(TCGContext *s)
-{
-#ifdef CONFIG_PLUGIN
- s->plugin_tb = g_new0(struct qemu_plugin_tb, 1);
- s->plugin_tb->insns =
- g_ptr_array_new_with_free_func(qemu_plugin_insn_cleanup_fn);
-#endif
-}
-
/*
* All TCG threads except the parent (i.e. the one that called tcg_context_init
* and registered the target's TCG globals) must register with this function
@@ -814,7 +805,6 @@ void tcg_register_thread(void)
qatomic_set(&tcg_ctxs[n], s);
if (n > 0) {
- alloc_tcg_plugin_context(s);
tcg_region_initial_alloc(s);
}
@@ -1361,8 +1351,6 @@ static void tcg_context_init(unsigned max_cpus)
indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i];
}
- alloc_tcg_plugin_context(s);
-
tcg_ctx = s;
/*
* In user-mode we simply share the init context among threads, since we
@@ -2251,7 +2239,8 @@ bool tcg_op_supported(TCGOpcode op)
static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs);
-static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args)
+static void tcg_gen_callN(void *func, TCGHelperInfo *info,
+ TCGTemp *ret, TCGTemp **args)
{
TCGv_i64 extend_free[MAX_CALL_IARGS];
int n_extend = 0;
@@ -2268,9 +2257,7 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args)
#ifdef CONFIG_PLUGIN
/* Flag helpers that may affect guest state */
- if (tcg_ctx->plugin_insn &&
- !(info->flags & TCG_CALL_PLUGIN) &&
- !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) {
+ if (tcg_ctx->plugin_insn && !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) {
tcg_ctx->plugin_insn->calls_helpers = true;
}
#endif
@@ -2329,7 +2316,7 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args)
g_assert_not_reached();
}
}
- op->args[pi++] = (uintptr_t)info->func;
+ op->args[pi++] = (uintptr_t)func;
op->args[pi++] = (uintptr_t)info;
tcg_debug_assert(pi == total_args);
@@ -2345,56 +2332,58 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, TCGTemp **args)
}
}
-void tcg_gen_call0(TCGHelperInfo *info, TCGTemp *ret)
+void tcg_gen_call0(void *func, TCGHelperInfo *info, TCGTemp *ret)
{
- tcg_gen_callN(info, ret, NULL);
+ tcg_gen_callN(func, info, ret, NULL);
}
-void tcg_gen_call1(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1)
+void tcg_gen_call1(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1)
{
- tcg_gen_callN(info, ret, &t1);
+ tcg_gen_callN(func, info, ret, &t1);
}
-void tcg_gen_call2(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2)
+void tcg_gen_call2(void *func, TCGHelperInfo *info, TCGTemp *ret,
+ TCGTemp *t1, TCGTemp *t2)
{
TCGTemp *args[2] = { t1, t2 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
-void tcg_gen_call3(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
- TCGTemp *t2, TCGTemp *t3)
+void tcg_gen_call3(void *func, TCGHelperInfo *info, TCGTemp *ret,
+ TCGTemp *t1, TCGTemp *t2, TCGTemp *t3)
{
TCGTemp *args[3] = { t1, t2, t3 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
-void tcg_gen_call4(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
- TCGTemp *t2, TCGTemp *t3, TCGTemp *t4)
+void tcg_gen_call4(void *func, TCGHelperInfo *info, TCGTemp *ret,
+ TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, TCGTemp *t4)
{
TCGTemp *args[4] = { t1, t2, t3, t4 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
-void tcg_gen_call5(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
+void tcg_gen_call5(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
TCGTemp *t2, TCGTemp *t3, TCGTemp *t4, TCGTemp *t5)
{
TCGTemp *args[5] = { t1, t2, t3, t4, t5 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
-void tcg_gen_call6(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1, TCGTemp *t2,
- TCGTemp *t3, TCGTemp *t4, TCGTemp *t5, TCGTemp *t6)
+void tcg_gen_call6(void *func, TCGHelperInfo *info, TCGTemp *ret,
+ TCGTemp *t1, TCGTemp *t2, TCGTemp *t3,
+ TCGTemp *t4, TCGTemp *t5, TCGTemp *t6)
{
TCGTemp *args[6] = { t1, t2, t3, t4, t5, t6 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
-void tcg_gen_call7(TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
+void tcg_gen_call7(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1,
TCGTemp *t2, TCGTemp *t3, TCGTemp *t4,
TCGTemp *t5, TCGTemp *t6, TCGTemp *t7)
{
TCGTemp *args[7] = { t1, t2, t3, t4, t5, t6, t7 };
- tcg_gen_callN(info, ret, args);
+ tcg_gen_callN(func, info, ret, args);
}
static void tcg_reg_alloc_start(TCGContext *s)
@@ -2539,6 +2528,15 @@ static const char bswap_flag_name[][6] = {
[TCG_BSWAP_IZ | TCG_BSWAP_OS] = "iz,os",
};
+#ifdef CONFIG_PLUGIN
+static const char * const plugin_from_name[] = {
+ "from-tb",
+ "from-insn",
+ "after-insn",
+ "after-tb",
+};
+#endif
+
static inline bool tcg_regset_single(TCGRegSet d)
{
return (d & (d - 1)) == 0;
@@ -2557,7 +2555,7 @@ static inline TCGReg tcg_regset_first(TCGRegSet d)
#define ne_fprintf(...) \
({ int ret_ = fprintf(__VA_ARGS__); ret_ >= 0 ? ret_ : 0; })
-static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
+void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
{
char buf[128];
TCGOp *op;
@@ -2713,6 +2711,24 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
i = k = 1;
}
break;
+#ifdef CONFIG_PLUGIN
+ case INDEX_op_plugin_cb:
+ {
+ TCGArg from = op->args[k++];
+ const char *name = NULL;
+
+ if (from < ARRAY_SIZE(plugin_from_name)) {
+ name = plugin_from_name[from];
+ }
+ if (name) {
+ col += ne_fprintf(f, "%s", name);
+ } else {
+ col += ne_fprintf(f, "$0x%" TCG_PRIlx, from);
+ }
+ i = 1;
+ }
+ break;
+#endif
default:
i = 0;
break;
diff --git a/tcg/tci.c b/tcg/tci.c
index 39adcb7d82..3afb223528 100644
--- a/tcg/tci.c
+++ b/tcg/tci.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "tcg/tcg.h"
+#include "tcg/helper-info.h"
#include "tcg/tcg-ldst.h"
#include <ffi.h>
diff --git a/tests/bench/bufferiszero-bench.c b/tests/bench/bufferiszero-bench.c
new file mode 100644
index 0000000000..222695c1fa
--- /dev/null
+++ b/tests/bench/bufferiszero-bench.c
@@ -0,0 +1,47 @@
+/*
+ * QEMU buffer_is_zero speed benchmark
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/units.h"
+
+static void test(const void *opaque)
+{
+ size_t max = 64 * KiB;
+ void *buf = g_malloc0(max);
+ int accel_index = 0;
+
+ do {
+ if (accel_index != 0) {
+ g_test_message("%s", ""); /* gnu_printf Werror for simple "" */
+ }
+ for (size_t len = 1 * KiB; len <= max; len *= 4) {
+ double total = 0.0;
+
+ g_test_timer_start();
+ do {
+ buffer_is_zero_ge256(buf, len);
+ total += len;
+ } while (g_test_timer_elapsed() < 0.5);
+
+ total /= MiB;
+ g_test_message("buffer_is_zero #%d: %2zuKB %8.0f MB/sec",
+ accel_index, len / (size_t)KiB,
+ total / g_test_timer_last());
+ }
+ accel_index++;
+ } while (test_buffer_is_zero_next_accel());
+
+ g_free(buf);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_data_func("/cutils/bufferiszero/speed", NULL, test);
+ return g_test_run();
+}
diff --git a/tests/bench/meson.build b/tests/bench/meson.build
index 7e76338a52..4cd7a2f6b5 100644
--- a/tests/bench/meson.build
+++ b/tests/bench/meson.build
@@ -21,6 +21,7 @@ benchs = {}
if have_block
benchs += {
+ 'bufferiszero-bench': [],
'benchmark-crypto-hash': [crypto],
'benchmark-crypto-hmac': [crypto],
'benchmark-crypto-cipher': [crypto],
diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker
index cd9d7af1ce..554464f31e 100644
--- a/tests/docker/dockerfiles/alpine.docker
+++ b/tests/docker/dockerfiles/alpine.docker
@@ -32,7 +32,6 @@ RUN apk update && \
findutils \
flex \
fuse3-dev \
- g++ \
gcc \
gcovr \
gettext \
@@ -110,7 +109,6 @@ RUN apk update && \
vte3-dev \
which \
xen-dev \
- xfsprogs-dev \
xorriso \
zlib-dev \
zlib-static \
@@ -119,10 +117,8 @@ RUN apk update && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
apk list --installed | sort > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker
index 6cf47ce786..0256865b9e 100644
--- a/tests/docker/dockerfiles/centos9.docker
+++ b/tests/docker/dockerfiles/centos9.docker
@@ -34,7 +34,6 @@ RUN dnf distro-sync -y && \
flex \
fuse3-devel \
gcc \
- gcc-c++ \
gettext \
git \
glib2-devel \
@@ -115,7 +114,6 @@ RUN dnf distro-sync -y && \
util-linux \
vte291-devel \
which \
- xfsprogs-devel \
xorriso \
zlib-devel \
zlib-static \
@@ -125,10 +123,8 @@ RUN dnf distro-sync -y && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
rpm -qa | sort > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker
index 2cc7a24d4d..6cc38a3633 100644
--- a/tests/docker/dockerfiles/debian-all-test-cross.docker
+++ b/tests/docker/dockerfiles/debian-all-test-cross.docker
@@ -68,6 +68,7 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools
ENV DEF_TARGET_LIST aarch64-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sparc64-linux-user
# As a final step configure the user (if env is defined)
+ENV MAKE /usr/bin/make
ARG USER
ARG UID
RUN if [ "${USER}" ]; then \
diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker
index d0b0e9778e..f8c61d1191 100644
--- a/tests/docker/dockerfiles/debian-amd64-cross.docker
+++ b/tests/docker/dockerfiles/debian-amd64-cross.docker
@@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-x86-64-linux-gnu \
gcc-x86-64-linux-gnu \
libaio-dev:amd64 \
libasan6:amd64 \
@@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:amd64 \
nettle-dev:amd64 \
systemtap-sdt-dev:amd64 \
- xfslibs-dev:amd64 \
zlib1g-dev:amd64 && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -167,9 +165,7 @@ cpu = 'x86_64'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/x86_64-linux-gnu-gcc
ENV ABI "x86_64-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker
index 8cb225740e..6510872279 100644
--- a/tests/docker/dockerfiles/debian-arm64-cross.docker
+++ b/tests/docker/dockerfiles/debian-arm64-cross.docker
@@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-aarch64-linux-gnu \
gcc-aarch64-linux-gnu \
libaio-dev:arm64 \
libasan6:arm64 \
@@ -148,7 +147,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:arm64 \
nettle-dev:arm64 \
systemtap-sdt-dev:arm64 \
- xfslibs-dev:arm64 \
zlib1g-dev:arm64 && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -166,9 +164,7 @@ cpu = 'aarch64'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/aarch64-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-gcc
ENV ABI "aarch64-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker
index e6f37418ed..f227d42987 100644
--- a/tests/docker/dockerfiles/debian-armel-cross.docker
+++ b/tests/docker/dockerfiles/debian-armel-cross.docker
@@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-arm-linux-gnueabi \
gcc-arm-linux-gnueabi \
libaio-dev:armel \
libasan6:armel \
@@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:armel \
nettle-dev:armel \
systemtap-sdt-dev:armel \
- xfslibs-dev:armel \
zlib1g-dev:armel && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -167,9 +165,7 @@ cpu = 'arm'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-gcc
ENV ABI "arm-linux-gnueabi"
diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker
index 407a014f57..58bdf07223 100644
--- a/tests/docker/dockerfiles/debian-armhf-cross.docker
+++ b/tests/docker/dockerfiles/debian-armhf-cross.docker
@@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-arm-linux-gnueabihf \
gcc-arm-linux-gnueabihf \
libaio-dev:armhf \
libasan6:armhf \
@@ -148,7 +147,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:armhf \
nettle-dev:armhf \
systemtap-sdt-dev:armhf \
- xfslibs-dev:armhf \
zlib1g-dev:armhf && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -166,9 +164,7 @@ cpu = 'armhf'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-gcc
ENV ABI "arm-linux-gnueabihf"
diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker
index 60bd8faa20..f2d40f2dee 100644
--- a/tests/docker/dockerfiles/debian-hexagon-cross.docker
+++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker
@@ -45,6 +45,7 @@ ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
RUN curl -#SL "$TOOLCHAIN_URL" | tar -xJC "$TOOLCHAIN_INSTALL"
ENV PATH $PATH:${TOOLCHAIN_INSTALL}/${TOOLCHAIN_BASENAME}/x86_64-linux-gnu/bin
+ENV MAKE /usr/bin/make
# As a final step configure the user (if env is defined)
ARG USER
ARG UID
diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker
index bdc9566b67..9f4102be8f 100644
--- a/tests/docker/dockerfiles/debian-i686-cross.docker
+++ b/tests/docker/dockerfiles/debian-i686-cross.docker
@@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-i686-linux-gnu \
gcc-i686-linux-gnu \
libaio-dev:i386 \
libasan6:i386 \
@@ -149,7 +148,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:i386 \
nettle-dev:i386 \
systemtap-sdt-dev:i386 \
- xfslibs-dev:i386 \
zlib1g-dev:i386 && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -167,9 +165,7 @@ cpu = 'i686'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-linux-gnu-gcc
ENV ABI "i686-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-legacy-test-cross.docker b/tests/docker/dockerfiles/debian-legacy-test-cross.docker
index 8cc68bc912..d75e0b85e2 100644
--- a/tests/docker/dockerfiles/debian-legacy-test-cross.docker
+++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker
@@ -42,6 +42,7 @@ RUN /usr/bin/pip3 install tomli
ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools
ENV DEF_TARGET_LIST alpha-linux-user,sh4-linux-user
+ENV MAKE /usr/bin/make
# As a final step configure the user (if env is defined)
ARG USER
ARG UID
diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker
index b25e779a2c..6a9197528b 100644
--- a/tests/docker/dockerfiles/debian-loongarch-cross.docker
+++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker
@@ -44,6 +44,7 @@ ENV LD_LIBRARY_PATH /opt/cross-tools/lib:/opt/cross-tools/loongarch64-unknown-li
ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools
ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch-softmmu
+ENV MAKE /usr/bin/make
# As a final step configure the user (if env is defined)
ARG USER
diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker
index 4d995d0b12..c861c3bd5b 100644
--- a/tests/docker/dockerfiles/debian-mips64el-cross.docker
+++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker
@@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-mips64el-linux-gnuabi64 \
gcc-mips64el-linux-gnuabi64 \
libaio-dev:mips64el \
libasound2-dev:mips64el \
@@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:mips64el \
nettle-dev:mips64el \
systemtap-sdt-dev:mips64el \
- xfslibs-dev:mips64el \
zlib1g-dev:mips64el && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -165,9 +163,7 @@ cpu = 'mips64el'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-gcc
ENV ABI "mips64el-linux-gnuabi64"
diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker
index 0cf803bda5..fe9415395e 100644
--- a/tests/docker/dockerfiles/debian-mipsel-cross.docker
+++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker
@@ -82,7 +82,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-mipsel-linux-gnu \
gcc-mipsel-linux-gnu \
libaio-dev:mipsel \
libasound2-dev:mipsel \
@@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:mipsel \
nettle-dev:mipsel \
systemtap-sdt-dev:mipsel \
- xfslibs-dev:mipsel \
zlib1g-dev:mipsel && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -165,9 +163,7 @@ cpu = 'mipsel'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/mipsel-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-gcc
ENV ABI "mipsel-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker
index 6180ec08c3..35c8ff0864 100644
--- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker
+++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker
@@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-powerpc64le-linux-gnu \
gcc-powerpc64le-linux-gnu \
libaio-dev:ppc64el \
libasan6:ppc64el \
@@ -147,7 +146,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:ppc64el \
nettle-dev:ppc64el \
systemtap-sdt-dev:ppc64el \
- xfslibs-dev:ppc64el \
zlib1g-dev:ppc64el && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -165,9 +163,7 @@ cpu = 'powerpc64le'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-gcc
ENV ABI "powerpc64le-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker
index 591572ae94..4d8ca83cb3 100644
--- a/tests/docker/dockerfiles/debian-riscv64-cross.docker
+++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker
@@ -51,7 +51,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-riscv64-linux-gnu \
gcc-riscv64-linux-gnu \
libc6-dev:riscv64 \
libfdt-dev:riscv64 \
@@ -74,9 +73,7 @@ cpu = 'riscv64'\n\
endian = 'little'\n" > /usr/local/share/meson/cross/riscv64-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/riscv64-linux-gnu-gcc
ENV ABI "riscv64-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker
index 90c8d3c7b8..bef9dff17a 100644
--- a/tests/docker/dockerfiles/debian-s390x-cross.docker
+++ b/tests/docker/dockerfiles/debian-s390x-cross.docker
@@ -79,7 +79,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
eatmydata apt-get dist-upgrade -y && \
eatmydata apt-get install --no-install-recommends -y dpkg-dev && \
eatmydata apt-get install --no-install-recommends -y \
- g++-s390x-linux-gnu \
gcc-s390x-linux-gnu \
libaio-dev:s390x \
libasan6:s390x \
@@ -146,7 +145,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
libzstd-dev:s390x \
nettle-dev:s390x \
systemtap-sdt-dev:s390x \
- xfslibs-dev:s390x \
zlib1g-dev:s390x && \
eatmydata apt-get autoremove -y && \
eatmydata apt-get autoclean -y && \
@@ -164,9 +162,7 @@ cpu = 's390x'\n\
endian = 'big'\n" > /usr/local/share/meson/cross/s390x-linux-gnu && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-cc && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-gcc
ENV ABI "s390x-linux-gnu"
diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker
index c597f8e16b..16276aa21d 100644
--- a/tests/docker/dockerfiles/debian-tricore-cross.docker
+++ b/tests/docker/dockerfiles/debian-tricore-cross.docker
@@ -44,6 +44,7 @@ RUN curl -#SL https://github.com/bkoppelmann/package_940/releases/download/trico
# This image can only build a very minimal QEMU as well as the tests
ENV DEF_TARGET_LIST tricore-softmmu
ENV QEMU_CONFIGURE_OPTS --disable-user --disable-tools --disable-fdt
+ENV MAKE /usr/bin/make
# As a final step configure the user (if env is defined)
ARG USER
ARG UID
diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker
index 72c25d63d9..413881899b 100644
--- a/tests/docker/dockerfiles/debian-xtensa-cross.docker
+++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker
@@ -27,6 +27,7 @@ RUN for cpu in $CPU_LIST; do \
done
ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-de233_fpu-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dsp3400-elf/bin
+ENV MAKE /usr/bin/make
# As a final step configure the user (if env is defined)
ARG USER
ARG UID
diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker
index 5722482e4c..63d7aac616 100644
--- a/tests/docker/dockerfiles/debian.docker
+++ b/tests/docker/dockerfiles/debian.docker
@@ -25,7 +25,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
- g++ \
gcc \
gcovr \
gettext \
@@ -129,7 +128,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
tar \
tesseract-ocr \
tesseract-ocr-eng \
- xfslibs-dev \
xorriso \
zlib1g-dev \
zstd && \
@@ -140,10 +138,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker
index f2899af410..97c9d37ede 100644
--- a/tests/docker/dockerfiles/fedora-cris-cross.docker
+++ b/tests/docker/dockerfiles/fedora-cris-cross.docker
@@ -4,6 +4,7 @@
FROM registry.fedoraproject.org/fedora:33
ENV PACKAGES gcc-cris-linux-gnu
+ENV MAKE /usr/bin/make
RUN dnf install -y $PACKAGES
RUN rpm -q $PACKAGES | sort > /packages.txt
# As a final step configure the user (if env is defined)
diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker
index d1a480fa71..0f78711876 100644
--- a/tests/docker/dockerfiles/fedora-win64-cross.docker
+++ b/tests/docker/dockerfiles/fedora-win64-cross.docker
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTO-GENERATED
#
-# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu
+# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-38 qemu,qemu-win-installer
#
# https://gitlab.com/libvirt/libvirt-ci
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 7e6ab0308a..098c894d10 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -41,7 +41,6 @@ exec "$@"\n' > /usr/bin/nosync && \
flex \
fuse3-devel \
gcc \
- gcc-c++ \
gcovr \
gettext \
git \
@@ -130,7 +129,6 @@ exec "$@"\n' > /usr/bin/nosync && \
vte291-devel \
which \
xen-devel \
- xfsprogs-devel \
xorriso \
zlib-devel \
zlib-static \
@@ -140,10 +138,8 @@ exec "$@"\n' > /usr/bin/nosync && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
rpm -qa | sort > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker
index c4055bdd10..836f531ac1 100644
--- a/tests/docker/dockerfiles/opensuse-leap.docker
+++ b/tests/docker/dockerfiles/opensuse-leap.docker
@@ -26,7 +26,6 @@ RUN zypper update -y && \
flex \
fuse3-devel \
gcc \
- gcc-c++ \
gcovr \
gettext-runtime \
git \
@@ -113,7 +112,6 @@ RUN zypper update -y && \
vte-devel \
which \
xen-devel \
- xfsprogs-devel \
xorriso \
zlib-devel \
zlib-devel-static \
@@ -122,10 +120,8 @@ RUN zypper update -y && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
rpm -qa | sort > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
RUN /usr/bin/pip3.11 install \
diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker
index b8e78331db..febd25b320 100644
--- a/tests/docker/dockerfiles/ubuntu2204.docker
+++ b/tests/docker/dockerfiles/ubuntu2204.docker
@@ -25,7 +25,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
exuberant-ctags \
findutils \
flex \
- g++ \
gcc \
gcovr \
gettext \
@@ -129,7 +128,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
tar \
tesseract-ocr \
tesseract-ocr-eng \
- xfslibs-dev \
xorriso \
zlib1g-dev \
zstd && \
@@ -140,10 +138,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
mkdir -p /usr/libexec/ccache-wrappers && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
- ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py
index 7f71d34da1..46fbf98f0c 100644
--- a/tests/guest-debug/test_gdbstub.py
+++ b/tests/guest-debug/test_gdbstub.py
@@ -57,4 +57,4 @@ def main(test, expected_arch=None):
pass
print("All tests complete: {} failures".format(fail_count))
- exit(fail_count)
+ gdb.execute(f"exit {fail_count}")
diff --git a/tests/lcitool/projects/qemu-minimal.yml b/tests/lcitool/projects/qemu-minimal.yml
index d44737dc1d..6bc232a1c3 100644
--- a/tests/lcitool/projects/qemu-minimal.yml
+++ b/tests/lcitool/projects/qemu-minimal.yml
@@ -7,7 +7,6 @@ packages:
- ccache
- findutils
- flex
- - g++
- gcc
- gcc-native
- glib2
diff --git a/tests/lcitool/projects/qemu-win-installer.yml b/tests/lcitool/projects/qemu-win-installer.yml
new file mode 100644
index 0000000000..86aa22297c
--- /dev/null
+++ b/tests/lcitool/projects/qemu-win-installer.yml
@@ -0,0 +1,4 @@
+# Additional packages that are required to build the code in qga/vss-win32/
+---
+packages:
+ - g++
diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml
index 149b15de57..7511ec7ccb 100644
--- a/tests/lcitool/projects/qemu.yml
+++ b/tests/lcitool/projects/qemu.yml
@@ -22,7 +22,6 @@ packages:
- findutils
- flex
- fuse3
- - g++
- gcc
- gcc-native
- gcovr
@@ -36,8 +35,8 @@ packages:
- hostname
- json-c
- libaio
- - libattr
- libasan
+ - libattr
- libbpf
- libc-static
- libcacard
@@ -55,6 +54,7 @@ packages:
- libjpeg
- libnfs
- libnuma
+ - libpipewire-dev
- libpmem
- libpng
- librbd
@@ -74,27 +74,26 @@ packages:
- llvm
- lttng-ust
- lzo
+ - make
+ - mesa-libgbm
+ - meson
- mtools
+ - ncursesw
- netcat
- nettle
- ninja
- nsis
- - make
- - mesa-libgbm
- - meson
- - ncursesw
- pam
- pcre-static
- pixman
- - libpipewire-dev
- pkg-config
- pulseaudio
- python3
- - python3-PyYAML
- python3-numpy
- python3-opencv
- python3-pillow
- python3-pip
+ - python3-PyYAML
- python3-sphinx
- python3-sphinx-rtd-theme
- python3-sqlite3
@@ -121,8 +120,7 @@ packages:
- vte
- which
- xen
- - xfsprogs
- xorriso
- - zstdtools
- zlib
- zlib-static
+ - zstdtools
diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh
index 24a735a3f2..789acefb75 100755
--- a/tests/lcitool/refresh
+++ b/tests/lcitool/refresh
@@ -43,12 +43,12 @@ def atomic_write(filename, content):
def generate(filename, cmd, trailer):
print("Generate %s" % filename)
- lcitool = subprocess.run(cmd, capture_output=True)
+ lcitool = subprocess.run(cmd, capture_output=True, encoding='utf8')
if lcitool.returncode != 0:
raise Exception("Failed to generate %s: %s" % (filename, lcitool.stderr))
- content = lcitool.stdout.decode("utf8")
+ content = lcitool.stdout
if trailer is not None:
content += trailer
atomic_write(filename, content)
@@ -192,6 +192,7 @@ try:
"s390x-softmmu,s390x-linux-user"))
generate_dockerfile("fedora-win64-cross", "fedora-38",
+ project='qemu,qemu-win-installer',
cross="mingw64",
trailer=cross_build("x86_64-w64-mingw32-",
"x86_64-softmmu"))
diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 0163e9b51c..cd63827b7d 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -20,8 +20,20 @@ typedef struct {
uint64_t count_insn_inline;
uint64_t count_mem;
uint64_t count_mem_inline;
+ uint64_t tb_cond_num_trigger;
+ uint64_t tb_cond_track_count;
+ uint64_t insn_cond_num_trigger;
+ uint64_t insn_cond_track_count;
} CPUCount;
+static const uint64_t cond_trigger_limit = 100;
+
+typedef struct {
+ uint64_t data_insn;
+ uint64_t data_tb;
+ uint64_t data_mem;
+} CPUData;
+
static struct qemu_plugin_scoreboard *counts;
static qemu_plugin_u64 count_tb;
static qemu_plugin_u64 count_tb_inline;
@@ -29,6 +41,14 @@ static qemu_plugin_u64 count_insn;
static qemu_plugin_u64 count_insn_inline;
static qemu_plugin_u64 count_mem;
static qemu_plugin_u64 count_mem_inline;
+static qemu_plugin_u64 tb_cond_num_trigger;
+static qemu_plugin_u64 tb_cond_track_count;
+static qemu_plugin_u64 insn_cond_num_trigger;
+static qemu_plugin_u64 insn_cond_track_count;
+static struct qemu_plugin_scoreboard *data;
+static qemu_plugin_u64 data_insn;
+static qemu_plugin_u64 data_tb;
+static qemu_plugin_u64 data_mem;
static uint64_t global_count_tb;
static uint64_t global_count_insn;
@@ -46,12 +66,19 @@ static void stats_insn(void)
const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
const uint64_t inl_per_vcpu =
qemu_plugin_u64_sum(count_insn_inline);
+ const uint64_t cond_num_trigger =
+ qemu_plugin_u64_sum(insn_cond_num_trigger);
+ const uint64_t cond_track_left = qemu_plugin_u64_sum(insn_cond_track_count);
+ const uint64_t conditional =
+ cond_num_trigger * cond_trigger_limit + cond_track_left;
printf("insn: %" PRIu64 "\n", expected);
printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+ printf("insn: %" PRIu64 " (cond cb)\n", conditional);
g_assert(expected > 0);
g_assert(per_vcpu == expected);
g_assert(inl_per_vcpu == expected);
+ g_assert(conditional == expected);
}
static void stats_tb(void)
@@ -60,12 +87,18 @@ static void stats_tb(void)
const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
const uint64_t inl_per_vcpu =
qemu_plugin_u64_sum(count_tb_inline);
+ const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger);
+ const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
+ const uint64_t conditional =
+ cond_num_trigger * cond_trigger_limit + cond_track_left;
printf("tb: %" PRIu64 "\n", expected);
printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+ printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
g_assert(expected > 0);
g_assert(per_vcpu == expected);
g_assert(inl_per_vcpu == expected);
+ g_assert(conditional == expected);
}
static void stats_mem(void)
@@ -94,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
- printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
- "insn (%" PRIu64 ", %" PRIu64 ") | "
+ const uint64_t tb_cond_trigger =
+ qemu_plugin_u64_get(tb_cond_num_trigger, i);
+ const uint64_t tb_cond_left =
+ qemu_plugin_u64_get(tb_cond_track_count, i);
+ const uint64_t insn_cond_trigger =
+ qemu_plugin_u64_get(insn_cond_num_trigger, i);
+ const uint64_t insn_cond_left =
+ qemu_plugin_u64_get(insn_cond_track_count, i);
+ printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
+ ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+ ") | "
+ "insn (%" PRIu64 ", %" PRIu64
+ ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+ ") | "
"mem (%" PRIu64 ", %" PRIu64 ")"
"\n",
- i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
+ i,
+ tb, tb_inline,
+ tb_cond_trigger, cond_trigger_limit, tb_cond_left,
+ insn, insn_inline,
+ insn_cond_trigger, cond_trigger_limit, insn_cond_left,
+ mem, mem_inline);
g_assert(tb == tb_inline);
g_assert(insn == insn_inline);
g_assert(mem == mem_inline);
+ g_assert(tb_cond_trigger == tb / cond_trigger_limit);
+ g_assert(tb_cond_left == tb % cond_trigger_limit);
+ g_assert(insn_cond_trigger == insn / cond_trigger_limit);
+ g_assert(insn_cond_left == insn % cond_trigger_limit);
}
stats_tb();
@@ -109,20 +163,41 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
stats_mem();
qemu_plugin_scoreboard_free(counts);
+ qemu_plugin_scoreboard_free(data);
}
static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
{
qemu_plugin_u64_add(count_tb, cpu_index, 1);
+ g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
g_mutex_lock(&tb_lock);
max_cpu_index = MAX(max_cpu_index, cpu_index);
global_count_tb++;
g_mutex_unlock(&tb_lock);
}
+static void vcpu_tb_cond_exec(unsigned int cpu_index, void *udata)
+{
+ g_assert(qemu_plugin_u64_get(tb_cond_track_count, cpu_index) ==
+ cond_trigger_limit);
+ g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
+ qemu_plugin_u64_set(tb_cond_track_count, cpu_index, 0);
+ qemu_plugin_u64_add(tb_cond_num_trigger, cpu_index, 1);
+}
+
+static void vcpu_insn_cond_exec(unsigned int cpu_index, void *udata)
+{
+ g_assert(qemu_plugin_u64_get(insn_cond_track_count, cpu_index) ==
+ cond_trigger_limit);
+ g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
+ qemu_plugin_u64_set(insn_cond_track_count, cpu_index, 0);
+ qemu_plugin_u64_add(insn_cond_num_trigger, cpu_index, 1);
+}
+
static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
{
qemu_plugin_u64_add(count_insn, cpu_index, 1);
+ g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
g_mutex_lock(&insn_lock);
global_count_insn++;
g_mutex_unlock(&insn_lock);
@@ -131,9 +206,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
static void vcpu_mem_access(unsigned int cpu_index,
qemu_plugin_meminfo_t info,
uint64_t vaddr,
- void *userdata)
+ void *udata)
{
qemu_plugin_u64_add(count_mem, cpu_index, 1);
+ g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
g_mutex_lock(&mem_lock);
global_count_mem++;
g_mutex_unlock(&mem_lock);
@@ -141,20 +217,47 @@ static void vcpu_mem_access(unsigned int cpu_index,
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
{
+ void *tb_store = tb;
+ qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+ tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
qemu_plugin_register_vcpu_tb_exec_cb(
- tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+ tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
+ qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+ tb, QEMU_PLUGIN_INLINE_ADD_U64, tb_cond_track_count, 1);
+ qemu_plugin_register_vcpu_tb_exec_cond_cb(
+ tb, vcpu_tb_cond_exec, QEMU_PLUGIN_CB_NO_REGS,
+ QEMU_PLUGIN_COND_EQ, tb_cond_track_count, cond_trigger_limit, tb_store);
+
for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
+ void *insn_store = insn;
+ void *mem_store = (char *)insn_store + 0xff;
+
+ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+ insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
+ (uintptr_t) insn_store);
qemu_plugin_register_vcpu_insn_exec_cb(
- insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+ insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
+
+ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+ insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_cond_track_count, 1);
+ qemu_plugin_register_vcpu_insn_exec_cond_cb(
+ insn, vcpu_insn_cond_exec, QEMU_PLUGIN_CB_NO_REGS,
+ QEMU_PLUGIN_COND_EQ, insn_cond_track_count, cond_trigger_limit,
+ insn_store);
+
+ qemu_plugin_register_vcpu_mem_inline_per_vcpu(
+ insn, QEMU_PLUGIN_MEM_RW,
+ QEMU_PLUGIN_INLINE_STORE_U64,
+ data_mem, (uintptr_t) mem_store);
qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access,
QEMU_PLUGIN_CB_NO_REGS,
- QEMU_PLUGIN_MEM_RW, 0);
+ QEMU_PLUGIN_MEM_RW, mem_store);
qemu_plugin_register_vcpu_mem_inline_per_vcpu(
insn, QEMU_PLUGIN_MEM_RW,
QEMU_PLUGIN_INLINE_ADD_U64,
@@ -179,6 +282,19 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
counts, CPUCount, count_insn_inline);
count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
counts, CPUCount, count_mem_inline);
+ tb_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct(
+ counts, CPUCount, tb_cond_num_trigger);
+ tb_cond_track_count = qemu_plugin_scoreboard_u64_in_struct(
+ counts, CPUCount, tb_cond_track_count);
+ insn_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct(
+ counts, CPUCount, insn_cond_num_trigger);
+ insn_cond_track_count = qemu_plugin_scoreboard_u64_in_struct(
+ counts, CPUCount, insn_cond_track_count);
+ data = qemu_plugin_scoreboard_new(sizeof(CPUData));
+ data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
+ data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
+ data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
+
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 40095431ae..7e3f9f4aa1 100755
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -48,7 +48,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_if(ifcond)
def visit_object_type(self, name, info, ifcond, features,
- base, members, variants):
+ base, members, branches):
print('object %s' % name)
if base:
print(' base %s' % base.name)
@@ -57,13 +57,14 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
% (m.name, m.type.name, m.optional))
self._print_if(m.ifcond, 8)
self._print_features(m.features, indent=8)
- self._print_variants(variants)
+ self._print_variants(branches)
self._print_if(ifcond)
self._print_features(features)
- def visit_alternate_type(self, name, info, ifcond, features, variants):
+ def visit_alternate_type(self, name, info, ifcond, features,
+ alternatives):
print('alternate %s' % name)
- self._print_variants(variants)
+ self._print_variants(alternatives)
self._print_if(ifcond)
self._print_features(features)
diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183
deleted file mode 100755
index b85770458e..0000000000
--- a/tests/qemu-iotests/183
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/env bash
-# group: rw migration quick
-#
-# Test old-style block migration (migrate -b)
-#
-# Copyright (C) 2017 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"
-
-status=1 # failure is the default!
-
-MIG_SOCKET="${SOCK_DIR}/migrate"
-
-_cleanup()
-{
- rm -f "${MIG_SOCKET}"
- _rm_test_img "${TEST_IMG}.dest"
- _cleanup_test_img
- _cleanup_qemu
-}
-trap "_cleanup; exit \$status" 0 1 2 3 15
-
-# get standard environment, filters and checks
-. ./common.rc
-. ./common.filter
-. ./common.qemu
-
-_supported_os Linux FreeBSD NetBSD
-_supported_fmt qcow2 raw qed quorum
-_supported_proto file fuse
-
-size=64M
-_make_test_img $size
-TEST_IMG="${TEST_IMG}.dest" _make_test_img $size
-
-echo
-echo === Starting VMs ===
-echo
-
-qemu_comm_method="qmp"
-
-_launch_qemu \
- -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk
-src=$QEMU_HANDLE
-_send_qemu_cmd $src "{ 'execute': 'qmp_capabilities' }" 'return'
-
-_launch_qemu \
- -drive file="${TEST_IMG}.dest",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk \
- -incoming "unix:${MIG_SOCKET}"
-dest=$QEMU_HANDLE
-_send_qemu_cmd $dest "{ 'execute': 'qmp_capabilities' }" 'return'
-
-echo
-echo === Write something on the source ===
-echo
-
-_send_qemu_cmd $src \
- "{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk \"write -P 0x55 0 64k\"' } }" \
- 'return'
-_send_qemu_cmd $src \
- "{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \
- 'return'
-
-echo
-echo === Do block migration to destination ===
-echo
-
-reply="$(_send_qemu_cmd $src \
- "{ 'execute': 'migrate',
- 'arguments': { 'uri': 'unix:${MIG_SOCKET}', 'blk': true } }" \
- 'return\|error' | _filter_migration_block_deprecated)"
-echo "$reply"
-if echo "$reply" | grep "compiled without old-style" > /dev/null; then
- _notrun "migrate -b support not compiled in"
-fi
-
-timeout_comm=$QEMU_COMM_TIMEOUT
-if [ "${VALGRIND_QEMU}" == "y" ]; then
- QEMU_COMM_TIMEOUT=4
-else
- QEMU_COMM_TIMEOUT=0.1
-fi
-qemu_cmd_repeat=50 silent=yes \
- _send_qemu_cmd $src "{ 'execute': 'query-migrate' }" '"status": "completed"'
-QEMU_COMM_TIMEOUT=$timeout_comm
-_send_qemu_cmd $src "{ 'execute': 'query-status' }" "return"
-
-echo
-echo === Do some I/O on the destination ===
-echo
-
-# It is important that we use the BlockBackend of the guest device here instead
-# of the node name, which would create a new BlockBackend and not test whether
-# the guest has the necessary permissions to access the image now
-silent=yes _send_qemu_cmd $dest "" "100 %"
-_send_qemu_cmd $dest "{ 'execute': 'query-status' }" "return"
-_send_qemu_cmd $dest \
- "{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \
- 'return'
-_send_qemu_cmd $dest \
- "{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk \"write -P 0x66 1M 64k\"' } }" \
- 'return'
-
-echo
-echo === Shut down and check image ===
-echo
-
-_send_qemu_cmd $src '{"execute":"quit"}' 'return'
-_send_qemu_cmd $dest '{"execute":"quit"}' 'return'
-wait=1 _cleanup_qemu
-
-_check_test_img
-TEST_IMG="${TEST_IMG}.dest" _check_test_img
-
-$QEMU_IO -c 'write -P 0x66 1M 64k' "$TEST_IMG" | _filter_qemu_io
-$QEMU_IMG compare "$TEST_IMG.dest" "$TEST_IMG"
-
-# success, all done
-echo "*** done"
-rm -f $seq.full
-status=0
diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out
deleted file mode 100644
index 8aef74a25d..0000000000
--- a/tests/qemu-iotests/183.out
+++ /dev/null
@@ -1,66 +0,0 @@
-QA output created by 183
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT.dest', fmt=IMGFMT size=67108864
-
-=== Starting VMs ===
-
-{ 'execute': 'qmp_capabilities' }
-{"return": {}}
-{ 'execute': 'qmp_capabilities' }
-{"return": {}}
-
-=== Write something on the source ===
-
-{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk "write -P 0x55 0 64k"' } }
-wrote 65536/65536 bytes at offset 0
-64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-{"return": ""}
-{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk "read -P 0x55 0 64k"' } }
-read 65536/65536 bytes at offset 0
-64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-{"return": ""}
-
-=== Do block migration to destination ===
-
-{ 'execute': 'migrate',
- 'arguments': { 'uri': 'unix:SOCK_DIR/migrate', 'blk': true } }
-{"return": {}}
-{ 'execute': 'query-status' }
-{"return": {"status": "postmigrate", "running": false}}
-
-=== Do some I/O on the destination ===
-
-{ 'execute': 'query-status' }
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESUME"}
-{"return": {"status": "running", "running": true}}
-{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk "read -P 0x55 0 64k"' } }
-read 65536/65536 bytes at offset 0
-64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-{"return": ""}
-{ 'execute': 'human-monitor-command',
- 'arguments': { 'command-line':
- 'qemu-io disk "write -P 0x66 1M 64k"' } }
-wrote 65536/65536 bytes at offset 1048576
-64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-{"return": ""}
-
-=== Shut down and check image ===
-
-{"execute":"quit"}
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
-{"return": {}}
-{"execute":"quit"}
-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}}
-{"return": {}}
-No errors were found on the image.
-No errors were found on the image.
-wrote 65536/65536 bytes at offset 1048576
-64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Images are identical.
-*** done
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
index 2846c83808..fc3c64bcb8 100644
--- a/tests/qemu-iotests/common.filter
+++ b/tests/qemu-iotests/common.filter
@@ -359,12 +359,5 @@ _filter_qcow2_compression_type_bit()
-e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/'
}
-# filter warnings caused for block migration deprecation
-_filter_migration_block_deprecated()
-{
- gsed -e '/warning: parameter .blk. is deprecated; use blockdev-mirror with NBD instead/d' \
- -e '/warning: block migration is deprecated; use blockdev-mirror with NBD instead/d'
-}
-
# make sure this script returns success
true
diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c
index 9d6e6190d5..966c65d5c3 100644
--- a/tests/qtest/arm-cpu-features.c
+++ b/tests/qtest/arm-cpu-features.c
@@ -632,6 +632,10 @@ int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
+ if (!qtest_has_machine("virt")) {
+ goto out;
+ }
+
if (qtest_has_accel("tcg")) {
qtest_add_data_func("/arm/query-cpu-model-expansion",
NULL, test_query_cpu_model_expansion);
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index e3b7d65fe5..df389adeeb 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -26,6 +26,14 @@ static const uint8_t bios_avr[] = {
0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */
};
+static const uint8_t bios_loongarch64[] = {
+ 0x0c, 0xc0, 0x3f, 0x14, /* lu12i.w $t0, 0x1fe00 */
+ 0x8c, 0x81, 0x87, 0x03, /* ori $t0, $t0, 0x1e0 */
+ 0x0d, 0x50, 0x81, 0x03, /* li.w $t1, 'T' */
+ 0x8d, 0x01, 0x00, 0x29, /* st.b $t1, $t0, 0 */
+ 0xff, 0xf3, 0xff, 0x53, /* b -16 # loop */
+};
+
static const uint8_t kernel_mcf5208[] = {
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
@@ -167,6 +175,8 @@ static const testdef_t tests[] = {
{ "sparc", "SS-600MP", "", "TMS390Z55" },
{ "sparc64", "sun4u", "", "UltraSPARC" },
{ "s390x", "s390-ccw-virtio", "", "device" },
+ { "loongarch64", "virt", "-cpu max", "TT", sizeof(bios_loongarch64),
+ NULL, bios_loongarch64 },
{ "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 },
{ "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube },
{ "microblaze", "petalogix-s3adsp1800", "", "TT",
diff --git a/tests/qtest/dm163-test.c b/tests/qtest/dm163-test.c
new file mode 100644
index 0000000000..3161c9208d
--- /dev/null
+++ b/tests/qtest/dm163-test.c
@@ -0,0 +1,194 @@
+/*
+ * QTest testcase for DM163
+ *
+ * Copyright (C) 2024 Samuel Tardieu <sam@rfc1149.net>
+ * Copyright (C) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
+ * Copyright (C) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+enum DM163_INPUTS {
+ SIN = 8,
+ DCK = 9,
+ RST_B = 10,
+ LAT_B = 11,
+ SELBK = 12,
+ EN_B = 13
+};
+
+#define DEVICE_NAME "/machine/dm163"
+#define GPIO_OUT(name, value) qtest_set_irq_in(qts, DEVICE_NAME, NULL, name, \
+ value)
+#define GPIO_PULSE(name) \
+ do { \
+ GPIO_OUT(name, 1); \
+ GPIO_OUT(name, 0); \
+ } while (0)
+
+
+static void rise_gpio_pin_dck(QTestState *qts)
+{
+ /* Configure output mode for pin PB1 */
+ qtest_writel(qts, 0x48000400, 0xFFFFFEB7);
+ /* Write 1 in ODR for PB1 */
+ qtest_writel(qts, 0x48000414, 0x00000002);
+}
+
+static void lower_gpio_pin_dck(QTestState *qts)
+{
+ /* Configure output mode for pin PB1 */
+ qtest_writel(qts, 0x48000400, 0xFFFFFEB7);
+ /* Write 0 in ODR for PB1 */
+ qtest_writel(qts, 0x48000414, 0x00000000);
+}
+
+static void rise_gpio_pin_selbk(QTestState *qts)
+{
+ /* Configure output mode for pin PC5 */
+ qtest_writel(qts, 0x48000800, 0xFFFFF7FF);
+ /* Write 1 in ODR for PC5 */
+ qtest_writel(qts, 0x48000814, 0x00000020);
+}
+
+static void lower_gpio_pin_selbk(QTestState *qts)
+{
+ /* Configure output mode for pin PC5 */
+ qtest_writel(qts, 0x48000800, 0xFFFFF7FF);
+ /* Write 0 in ODR for PC5 */
+ qtest_writel(qts, 0x48000814, 0x00000000);
+}
+
+static void rise_gpio_pin_lat_b(QTestState *qts)
+{
+ /* Configure output mode for pin PC4 */
+ qtest_writel(qts, 0x48000800, 0xFFFFFDFF);
+ /* Write 1 in ODR for PC4 */
+ qtest_writel(qts, 0x48000814, 0x00000010);
+}
+
+static void lower_gpio_pin_lat_b(QTestState *qts)
+{
+ /* Configure output mode for pin PC4 */
+ qtest_writel(qts, 0x48000800, 0xFFFFFDFF);
+ /* Write 0 in ODR for PC4 */
+ qtest_writel(qts, 0x48000814, 0x00000000);
+}
+
+static void rise_gpio_pin_rst_b(QTestState *qts)
+{
+ /* Configure output mode for pin PC3 */
+ qtest_writel(qts, 0x48000800, 0xFFFFFF7F);
+ /* Write 1 in ODR for PC3 */
+ qtest_writel(qts, 0x48000814, 0x00000008);
+}
+
+static void lower_gpio_pin_rst_b(QTestState *qts)
+{
+ /* Configure output mode for pin PC3 */
+ qtest_writel(qts, 0x48000800, 0xFFFFFF7F);
+ /* Write 0 in ODR for PC3 */
+ qtest_writel(qts, 0x48000814, 0x00000000);
+}
+
+static void rise_gpio_pin_sin(QTestState *qts)
+{
+ /* Configure output mode for pin PA4 */
+ qtest_writel(qts, 0x48000000, 0xFFFFFDFF);
+ /* Write 1 in ODR for PA4 */
+ qtest_writel(qts, 0x48000014, 0x00000010);
+}
+
+static void lower_gpio_pin_sin(QTestState *qts)
+{
+ /* Configure output mode for pin PA4 */
+ qtest_writel(qts, 0x48000000, 0xFFFFFDFF);
+ /* Write 0 in ODR for PA4 */
+ qtest_writel(qts, 0x48000014, 0x00000000);
+}
+
+static void test_dm163_bank(const void *opaque)
+{
+ const unsigned bank = (uintptr_t) opaque;
+ const int width = bank ? 192 : 144;
+
+ QTestState *qts = qtest_initf("-M b-l475e-iot01a");
+ qtest_irq_intercept_out_named(qts, DEVICE_NAME, "sout");
+ GPIO_OUT(RST_B, 1);
+ GPIO_OUT(EN_B, 0);
+ GPIO_OUT(DCK, 0);
+ GPIO_OUT(SELBK, bank);
+ GPIO_OUT(LAT_B, 1);
+
+ /* Fill bank with zeroes */
+ GPIO_OUT(SIN, 0);
+ for (int i = 0; i < width; i++) {
+ GPIO_PULSE(DCK);
+ }
+ /* Fill bank with ones, check that we get the previous zeroes */
+ GPIO_OUT(SIN, 1);
+ for (int i = 0; i < width; i++) {
+ GPIO_PULSE(DCK);
+ g_assert(!qtest_get_irq(qts, 0));
+ }
+
+ /* Pulse one more bit in the bank, check that we get a one */
+ GPIO_PULSE(DCK);
+ g_assert(qtest_get_irq(qts, 0));
+
+ qtest_quit(qts);
+}
+
+static void test_dm163_gpio_connection(void)
+{
+ QTestState *qts = qtest_init("-M b-l475e-iot01a");
+ qtest_irq_intercept_in(qts, DEVICE_NAME);
+
+ g_assert_false(qtest_get_irq(qts, SIN));
+ g_assert_false(qtest_get_irq(qts, DCK));
+ g_assert_false(qtest_get_irq(qts, RST_B));
+ g_assert_false(qtest_get_irq(qts, LAT_B));
+ g_assert_false(qtest_get_irq(qts, SELBK));
+
+ rise_gpio_pin_dck(qts);
+ g_assert_true(qtest_get_irq(qts, DCK));
+ lower_gpio_pin_dck(qts);
+ g_assert_false(qtest_get_irq(qts, DCK));
+
+ rise_gpio_pin_lat_b(qts);
+ g_assert_true(qtest_get_irq(qts, LAT_B));
+ lower_gpio_pin_lat_b(qts);
+ g_assert_false(qtest_get_irq(qts, LAT_B));
+
+ rise_gpio_pin_selbk(qts);
+ g_assert_true(qtest_get_irq(qts, SELBK));
+ lower_gpio_pin_selbk(qts);
+ g_assert_false(qtest_get_irq(qts, SELBK));
+
+ rise_gpio_pin_rst_b(qts);
+ g_assert_true(qtest_get_irq(qts, RST_B));
+ lower_gpio_pin_rst_b(qts);
+ g_assert_false(qtest_get_irq(qts, RST_B));
+
+ rise_gpio_pin_sin(qts);
+ g_assert_true(qtest_get_irq(qts, SIN));
+ lower_gpio_pin_sin(qts);
+ g_assert_false(qtest_get_irq(qts, SIN));
+
+ g_assert_false(qtest_get_irq(qts, DCK));
+ g_assert_false(qtest_get_irq(qts, LAT_B));
+ g_assert_false(qtest_get_irq(qts, SELBK));
+ g_assert_false(qtest_get_irq(qts, RST_B));
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_data_func("/dm163/bank0", (void *)0, test_dm163_bank);
+ qtest_add_data_func("/dm163/bank1", (void *)1, test_dm163_bank);
+ qtest_add_func("/dm163/gpio_connection", test_dm163_gpio_connection);
+ return g_test_run();
+}
diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c
index 8a6f3ac963..7b67a4bbee 100644
--- a/tests/qtest/drive_del-test.c
+++ b/tests/qtest/drive_del-test.c
@@ -173,7 +173,7 @@ static void test_drive_without_dev(void)
QTestState *qts;
/* Start with an empty drive */
- qts = qtest_init("-drive if=none,id=drive0");
+ qts = qtest_init("-drive if=none,id=drive0 -M none");
/* Delete the drive */
drive_del(qts);
@@ -192,6 +192,11 @@ static void test_after_failed_device_add(void)
QDict *response;
QTestState *qts;
+ if (!has_device_builtin("virtio-blk")) {
+ g_test_skip("Device virtio-blk is not available");
+ return;
+ }
+
snprintf(driver, sizeof(driver), "virtio-blk-%s",
qvirtio_get_dev_type());
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index d6b4f6e36a..90ba6b298b 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -34,7 +34,8 @@
#include "hw/pci/pci_ids.h"
#include "hw/pci/pci_regs.h"
-#define TEST_IMAGE_SIZE 64 * 1024 * 1024
+/* Specified by ATA (physical) CHS geometry for ~64 MiB device. */
+#define TEST_IMAGE_SIZE ((130 * 16 * 63) * 512)
#define IDE_PCI_DEV 1
#define IDE_PCI_FUNC 1
@@ -88,11 +89,13 @@ enum {
enum {
CMD_DSM = 0x06,
CMD_DIAGNOSE = 0x90,
+ CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */
CMD_READ_DMA = 0xc8,
CMD_WRITE_DMA = 0xca,
CMD_FLUSH_CACHE = 0xe7,
CMD_IDENTIFY = 0xec,
CMD_PACKET = 0xa0,
+ CMD_READ_NATIVE = 0xf8, /* READ NATIVE MAX ADDRESS */
CMDF_ABORT = 0x100,
CMDF_NO_BM = 0x200,
@@ -560,6 +563,46 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes)
}
}
+static void test_specify(void)
+{
+ QTestState *qts;
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
+ uint16_t cyls;
+ uint8_t heads, spt;
+
+ qts = ide_test_start(
+ "-blockdev driver=file,node-name=hda,filename=%s "
+ "-device ide-hd,drive=hda,bus=ide.0,unit=0 ",
+ tmp_path[0]);
+
+ dev = get_pci_device(qts, &bmdma_bar, &ide_bar);
+
+ /* Initialize drive with zero sectors per track and one head. */
+ qpci_io_writeb(dev, ide_bar, reg_nsectors, 0);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP);
+
+ /* READ NATIVE MAX ADDRESS (CHS mode). */
+ qpci_io_writeb(dev, ide_bar, reg_device, 0xa0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ_NATIVE);
+
+ heads = qpci_io_readb(dev, ide_bar, reg_device) & 0xf;
+ ++heads;
+ g_assert_cmpint(heads, ==, 16);
+
+ cyls = qpci_io_readb(dev, ide_bar, reg_lba_high) << 8;
+ cyls |= qpci_io_readb(dev, ide_bar, reg_lba_middle);
+ ++cyls;
+ g_assert_cmpint(cyls, ==, 130);
+
+ spt = qpci_io_readb(dev, ide_bar, reg_lba_low);
+ g_assert_cmpint(spt, ==, 63);
+
+ ide_test_quit(qts);
+ free_pci_device(dev);
+}
+
static void test_identify(void)
{
QTestState *qts;
@@ -1077,6 +1120,8 @@ int main(int argc, char **argv)
/* Run the tests */
g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/ide/read_native", test_specify);
+
qtest_add_func("/ide/identify", test_identify);
qtest_add_func("/ide/diagnostic", test_diagnostic);
diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c
index b9cd209165..605797ab78 100644
--- a/tests/qtest/m48t59-test.c
+++ b/tests/qtest/m48t59-test.c
@@ -262,11 +262,12 @@ int main(int argc, char **argv)
base_setup();
g_test_init(&argc, &argv, NULL);
-
- if (g_test_slow()) {
- /* Do not run this in timing-sensitive environments */
- qtest_add_func("/rtc/bcd-check-time", bcd_check_time);
+ if (qtest_has_machine(base_machine)) {
+ if (g_test_slow()) {
+ /* Do not run this in timing-sensitive environments */
+ qtest_add_func("/rtc/bcd-check-time", bcd_check_time);
+ }
+ qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
}
- qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
return g_test_run();
}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index b128fa5a4b..86293051dc 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -139,6 +139,9 @@ qtests_hppa = ['boot-serial-test'] + \
qtests_filter + \
(config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : [])
+qtests_loongarch64 = qtests_filter + \
+ ['boot-serial-test']
+
qtests_m68k = ['boot-serial-test'] + \
qtests_filter
@@ -224,6 +227,8 @@ qtests_arm = \
(config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \
(config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \
(config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and
+ config_all_devices.has_key('CONFIG_DM163')? ['dm163-test'] : []) + \
['arm-cpu-features',
'boot-serial-test']
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 5d6d8cd634..b7e3406471 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -427,38 +427,6 @@ static void migrate_set_parameter_str(QTestState *who, const char *parameter,
migrate_check_parameter_str(who, parameter, value);
}
-static long long migrate_get_parameter_bool(QTestState *who,
- const char *parameter)
-{
- QDict *rsp;
- int result;
-
- rsp = qtest_qmp_assert_success_ref(
- who, "{ 'execute': 'query-migrate-parameters' }");
- result = qdict_get_bool(rsp, parameter);
- qobject_unref(rsp);
- return !!result;
-}
-
-static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
- int value)
-{
- int result;
-
- result = migrate_get_parameter_bool(who, parameter);
- g_assert_cmpint(result, ==, value);
-}
-
-static void migrate_set_parameter_bool(QTestState *who, const char *parameter,
- int value)
-{
- qtest_qmp_assert_success(who,
- "{ 'execute': 'migrate-set-parameters',"
- "'arguments': { %s: %i } }",
- parameter, value);
- migrate_check_parameter_bool(who, parameter, value);
-}
-
static void migrate_ensure_non_converge(QTestState *who)
{
/* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
@@ -813,6 +781,12 @@ static int test_migrate_start(QTestState **from, QTestState **to,
kvm_opts = ",dirty-ring-size=4096";
}
+ if (!qtest_has_machine(machine_alias)) {
+ g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias);
+ g_test_skip(msg);
+ return -1;
+ }
+
machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC,
QEMU_ENV_DST);
@@ -1240,36 +1214,6 @@ test_migrate_tls_x509_finish(QTestState *from,
#endif /* CONFIG_TASN1 */
#endif /* CONFIG_GNUTLS */
-static void *
-test_migrate_compress_start(QTestState *from,
- QTestState *to)
-{
- migrate_set_parameter_int(from, "compress-level", 1);
- migrate_set_parameter_int(from, "compress-threads", 4);
- migrate_set_parameter_bool(from, "compress-wait-thread", true);
- migrate_set_parameter_int(to, "decompress-threads", 4);
-
- migrate_set_capability(from, "compress", true);
- migrate_set_capability(to, "compress", true);
-
- return NULL;
-}
-
-static void *
-test_migrate_compress_nowait_start(QTestState *from,
- QTestState *to)
-{
- migrate_set_parameter_int(from, "compress-level", 9);
- migrate_set_parameter_int(from, "compress-threads", 1);
- migrate_set_parameter_bool(from, "compress-wait-thread", false);
- migrate_set_parameter_int(to, "decompress-threads", 1);
-
- migrate_set_capability(from, "compress", true);
- migrate_set_capability(to, "compress", true);
-
- return NULL;
-}
-
static int migrate_postcopy_prepare(QTestState **from_ptr,
QTestState **to_ptr,
MigrateCommon *args)
@@ -1370,15 +1314,6 @@ static void test_postcopy_suspend(void)
test_postcopy_common(&args);
}
-static void test_postcopy_compress(void)
-{
- MigrateCommon args = {
- .start_hook = test_migrate_compress_start
- };
-
- test_postcopy_common(&args);
-}
-
static void test_postcopy_preempt(void)
{
MigrateCommon args = {
@@ -1561,15 +1496,6 @@ static void test_postcopy_recovery(void)
test_postcopy_recovery_common(&args);
}
-static void test_postcopy_recovery_compress(void)
-{
- MigrateCommon args = {
- .start_hook = test_migrate_compress_start
- };
-
- test_postcopy_recovery_common(&args);
-}
-
#ifndef _WIN32
static void test_postcopy_recovery_double_fail(void)
{
@@ -1678,7 +1604,7 @@ static void test_analyze_script(void)
}
g_assert(waitpid(pid, &wstatus, 0) == pid);
- if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
g_test_message("Failed to analyze the migration stream");
g_test_fail();
}
@@ -2027,48 +1953,6 @@ static void test_precopy_unix_xbzrle(void)
test_precopy_common(&args);
}
-static void test_precopy_unix_compress(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_compress_start,
- /*
- * Test that no invalid thread state is left over from
- * the previous iteration.
- */
- .iterations = 2,
- /*
- * We make sure the compressor can always work well even if guest
- * memory is changing. See commit 34ab9e9743 where we used to fix
- * a bug when only trigger-able with guest memory changing.
- */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
-static void test_precopy_unix_compress_nowait(void)
-{
- g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
- MigrateCommon args = {
- .connect_uri = uri,
- .listen_uri = uri,
- .start_hook = test_migrate_compress_nowait_start,
- /*
- * Test that no invalid thread state is left over from
- * the previous iteration.
- */
- .iterations = 2,
- /* Same reason for the wait version of precopy compress test */
- .live = true,
- };
-
- test_precopy_common(&args);
-}
-
static void test_precopy_file(void)
{
g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs,
@@ -3553,6 +3437,20 @@ int main(int argc, char **argv)
arch = qtest_get_arch();
is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64");
+ tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err);
+ if (!tmpfs) {
+ g_test_message("Can't create temporary directory in %s: %s",
+ g_get_tmp_dir(), err->message);
+ }
+ g_assert(tmpfs);
+
+ module_call_init(MODULE_INIT_QOM);
+
+ migration_test_add("/migration/bad_dest", test_baddest);
+#ifndef _WIN32
+ migration_test_add("/migration/analyze-script", test_analyze_script);
+#endif
+
/*
* On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG
* is touchy due to race conditions on dirty bits (especially on PPC for
@@ -3560,8 +3458,8 @@ int main(int argc, char **argv)
*/
if (g_str_equal(arch, "ppc64") &&
(!has_kvm || access("/sys/module/kvm_hv", F_OK))) {
- g_test_message("Skipping test: kvm_hv not available");
- return g_test_run();
+ g_test_message("Skipping tests: kvm_hv not available");
+ goto test_add_done;
}
/*
@@ -3569,18 +3467,9 @@ int main(int argc, char **argv)
* there until the problems are resolved
*/
if (g_str_equal(arch, "s390x") && !has_kvm) {
- g_test_message("Skipping test: s390x host with KVM is required");
- return g_test_run();
- }
-
- tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err);
- if (!tmpfs) {
- g_test_message("Can't create temporary directory in %s: %s",
- g_get_tmp_dir(), err->message);
+ g_test_message("Skipping tests: s390x host with KVM is required");
+ goto test_add_done;
}
- g_assert(tmpfs);
-
- module_call_init(MODULE_INIT_QOM);
if (is_x86) {
migration_test_add("/migration/precopy/unix/suspend/live",
@@ -3597,12 +3486,6 @@ int main(int argc, char **argv)
test_postcopy_preempt);
migration_test_add("/migration/postcopy/preempt/recovery/plain",
test_postcopy_preempt_recovery);
- if (getenv("QEMU_TEST_FLAKY_TESTS")) {
- migration_test_add("/migration/postcopy/compress/plain",
- test_postcopy_compress);
- migration_test_add("/migration/postcopy/recovery/compress/plain",
- test_postcopy_recovery_compress);
- }
#ifndef _WIN32
migration_test_add("/migration/postcopy/recovery/double-failures",
test_postcopy_recovery_double_fail);
@@ -3613,27 +3496,10 @@ int main(int argc, char **argv)
}
}
- migration_test_add("/migration/bad_dest", test_baddest);
-#ifndef _WIN32
- if (!g_str_equal(arch, "s390x")) {
- migration_test_add("/migration/analyze-script", test_analyze_script);
- }
-#endif
migration_test_add("/migration/precopy/unix/plain",
test_precopy_unix_plain);
migration_test_add("/migration/precopy/unix/xbzrle",
test_precopy_unix_xbzrle);
- /*
- * Compression fails from time to time.
- * Put test here but don't enable it until everything is fixed.
- */
- if (getenv("QEMU_TEST_FLAKY_TESTS")) {
- migration_test_add("/migration/precopy/unix/compress/wait",
- test_precopy_unix_compress);
- migration_test_add("/migration/precopy/unix/compress/nowait",
- test_precopy_unix_compress_nowait);
- }
-
migration_test_add("/migration/precopy/file",
test_precopy_file);
migration_test_add("/migration/precopy/file/offset",
@@ -3786,6 +3652,8 @@ int main(int argc, char **argv)
test_vcpu_dirty_limit);
}
+test_add_done:
+
ret = g_test_run();
g_assert_cmpint(ret, ==, 0);
diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c
index 4f4404a4b1..7aa262dbb9 100644
--- a/tests/qtest/numa-test.c
+++ b/tests/qtest/numa-test.c
@@ -558,6 +558,9 @@ int main(int argc, char **argv)
}
if (g_str_equal(arch, "aarch64")) {
+ if (!qtest_has_machine("virt")) {
+ goto out;
+ }
g_string_append(args, " -machine virt");
}
@@ -590,5 +593,6 @@ int main(int argc, char **argv)
aarch64_numa_cpu);
}
+out:
return g_test_run();
}
diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c
index 008d189b0f..5ad6821f7a 100644
--- a/tests/qtest/nvme-test.c
+++ b/tests/qtest/nvme-test.c
@@ -13,7 +13,7 @@
#include "libqtest.h"
#include "libqos/qgraph.h"
#include "libqos/pci.h"
-#include "include/block/nvme.h"
+#include "block/nvme.h"
typedef struct QNvme QNvme;
diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c
index 95e82f9472..82ec3f0671 100644
--- a/tests/qtest/ufs-test.c
+++ b/tests/qtest/ufs-test.c
@@ -13,7 +13,7 @@
#include "libqos/qgraph.h"
#include "libqos/pci.h"
#include "scsi/constants.h"
-#include "include/block/ufs.h"
+#include "block/ufs.h"
/* Test images sizes in Bytes */
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target
index 4c9264057f..39e01ce49d 100644
--- a/tests/tcg/arm/Makefile.softmmu-target
+++ b/tests/tcg/arm/Makefile.softmmu-target
@@ -16,7 +16,7 @@ test-armv6m-undef: test-armv6m-undef.S
$< -o $@ -nostdlib -N -static \
-T $(ARM_SRC)/$@.ld
-run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel
+run-test-armv6m-undef: QEMU_OPTS=-semihosting-config enable=on,target=native,chardev=output -M microbit -kernel
ARM_TESTS+=test-armv6m-undef
diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c
index b45170acd1..1fe14b5158 100644
--- a/tests/tcg/hexagon/hvx_misc.c
+++ b/tests/tcg/hexagon/hvx_misc.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2021-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ * Copyright(c) 2021-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
*
* 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
@@ -231,6 +231,7 @@ static void test_masked_store(bool invert)
static void test_new_value_store(void)
{
void *p0 = buffer0;
+ void *p1 = buffer1;
void *pout = output;
asm("{\n\t"
@@ -242,6 +243,19 @@ static void test_new_value_store(void)
expect[0] = buffer0[0];
check_output_w(__LINE__, 1);
+
+ /* Test the .new read from the high half of a pair */
+ asm("v7 = vmem(%0 + #0)\n\t"
+ "v12 = vmem(%1 + #0)\n\t"
+ "{\n\t"
+ " v5:4 = vcombine(v12, v7)\n\t"
+ " vmem(%2 + #0) = v5.new\n\t"
+ "}\n\t"
+ : : "r"(p0), "r"(p1), "r"(pout) : "v4", "v5", "v7", "v12", "memory");
+
+ expect[0] = buffer1[0];
+
+ check_output_w(__LINE__, 1);
}
static void test_max_temps()
diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c
index 864c4e620d..ce3bf74b5a 100644
--- a/tests/tcg/i386/test-i386.c
+++ b/tests/tcg/i386/test-i386.c
@@ -715,6 +715,30 @@ void test_mul(void)
printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\
}
+void test_xcnt(void)
+{
+ TEST_BSX(tzcntw, "w", 0);
+ TEST_BSX(tzcntw, "w", 0x12340128);
+ TEST_BSX(lzcntw, "w", 0);
+ TEST_BSX(lzcntw, "w", 0x12340128);
+ TEST_BSX(popcntw, "w", 0);
+ TEST_BSX(popcntw, "w", 0x12340128);
+ TEST_BSX(tzcntl, "k", 0);
+ TEST_BSX(tzcntl, "k", 0x00340128);
+ TEST_BSX(lzcntl, "k", 0);
+ TEST_BSX(lzcntl, "k", 0x00340128);
+ TEST_BSX(popcntl, "k", 0);
+ TEST_BSX(popcntl, "k", 0x00340128);
+#if defined(__x86_64__)
+ TEST_BSX(tzcntq, "", 0);
+ TEST_BSX(tzcntq, "", 0x003401281234);
+ TEST_BSX(lzcntq, "", 0);
+ TEST_BSX(lzcntq, "", 0x003401281234);
+ TEST_BSX(popcntq, "", 0);
+ TEST_BSX(popcntq, "", 0x003401281234);
+#endif
+}
+
void test_bsx(void)
{
TEST_BSX(bsrw, "w", 0);
@@ -2162,6 +2186,7 @@ int main(int argc, char **argv)
func();
}
test_bsx();
+ test_xcnt();
test_mul();
test_jcc();
test_loop();
diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target
index 4d09291c0c..7852fa62d8 100644
--- a/tests/tcg/sh4/Makefile.target
+++ b/tests/tcg/sh4/Makefile.target
@@ -17,3 +17,9 @@ TESTS += test-macl
test-macw: CFLAGS += -O -g
TESTS += test-macw
+
+test-addv: CFLAGS += -O -g
+TESTS += test-addv
+
+test-subv: CFLAGS += -O -g
+TESTS += test-subv
diff --git a/tests/tcg/sh4/test-addv.c b/tests/tcg/sh4/test-addv.c
new file mode 100644
index 0000000000..ca87fe746a
--- /dev/null
+++ b/tests/tcg/sh4/test-addv.c
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void addv(const int a, const int b, const int res, const int carry)
+{
+ int o = a, c;
+
+ asm volatile("addv %2,%0\n"
+ "movt %1\n"
+ : "+r"(o), "=r"(c) : "r"(b) : );
+
+ if (c != carry || o != res) {
+ printf("ADDV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry);
+ abort();
+ }
+}
+
+int main(void)
+{
+ addv(INT_MAX, 1, INT_MIN, 1);
+ addv(INT_MAX - 1, 1, INT_MAX, 0);
+
+ return 0;
+}
diff --git a/tests/tcg/sh4/test-subv.c b/tests/tcg/sh4/test-subv.c
new file mode 100644
index 0000000000..a3c2db96e4
--- /dev/null
+++ b/tests/tcg/sh4/test-subv.c
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void subv(const int a, const int b, const int res, const int carry)
+{
+ int o = a, c;
+
+ asm volatile("subv %2,%0\n"
+ "movt %1\n"
+ : "+r"(o), "=r"(c) : "r"(b) : );
+
+ if (c != carry || o != res) {
+ printf("SUBV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry);
+ abort();
+ }
+}
+
+int main(void)
+{
+ subv(INT_MIN, 1, INT_MAX, 1);
+ subv(INT_MAX, -1, INT_MIN, 1);
+ subv(INT_MAX, 1, INT_MAX - 1, 0);
+ subv(0, 1, -1, 0);
+ subv(-1, -1, 0, 0);
+
+ return 0;
+}
diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c
index 8994337e12..9fdba24fce 100644
--- a/tests/unit/test-smp-parse.c
+++ b/tests/unit/test-smp-parse.c
@@ -330,6 +330,14 @@ static const struct SMPTestData data_generic_valid[] = {
.config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
.expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
.expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
+ }, {
+ /*
+ * Unsupported parameters are always allowed to be set to '1'
+ * config: -smp 8,books=1,drawers=1,sockets=2,modules=1,dies=1,cores=2,threads=2,maxcpus=8
+ * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
+ .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 2, 2, 8),
+ .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
+ .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
},
};
@@ -337,21 +345,21 @@ static const struct SMPTestData data_generic_invalid[] = {
{
/* config: -smp 2,dies=2 */
.config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
- .expect_error = "dies not supported by this machine's CPU topology",
+ .expect_error = "dies > 1 not supported by this machine's CPU topology",
}, {
/* config: -smp 2,clusters=2 */
.config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
- .expect_error = "clusters not supported by this machine's CPU topology",
+ .expect_error = "clusters > 1 not supported by this machine's CPU topology",
}, {
/* config: -smp 2,books=2 */
.config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, F, 0, T, 2, F,
0, F, 0, F, 0, F, 0),
- .expect_error = "books not supported by this machine's CPU topology",
+ .expect_error = "books > 1 not supported by this machine's CPU topology",
}, {
/* config: -smp 2,drawers=2 */
.config = SMP_CONFIG_WITH_BOOKS_DRAWERS(T, 2, T, 2, F, 0, F,
0, F, 0, F, 0, F, 0),
- .expect_error = "drawers not supported by this machine's CPU topology",
+ .expect_error = "drawers > 1 not supported by this machine's CPU topology",
}, {
/* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
.config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 25e0db9dd0..981615a8b9 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -50,23 +50,10 @@
#include <Carbon/Carbon.h>
#include "hw/core/cpu.h"
-#ifndef MAC_OS_X_VERSION_10_13
-#define MAC_OS_X_VERSION_10_13 101300
-#endif
-
#ifndef MAC_OS_VERSION_14_0
#define MAC_OS_VERSION_14_0 140000
#endif
-/* 10.14 deprecates NSOnState and NSOffState in favor of
- * NSControlStateValueOn/Off, which were introduced in 10.13.
- * Define for older versions
- */
-#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
-#define NSControlStateValueOn NSOnState
-#define NSControlStateValueOff NSOffState
-#endif
-
//#define DEBUG
#ifdef DEBUG
diff --git a/ui/console.c b/ui/console.c
index 43226c5c14..1b2cd0c736 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1459,7 +1459,7 @@ int qemu_console_get_width(QemuConsole *con, int fallback)
}
switch (con->scanout.kind) {
case SCANOUT_DMABUF:
- return con->scanout.dmabuf->width;
+ return qemu_dmabuf_get_width(con->scanout.dmabuf);
case SCANOUT_TEXTURE:
return con->scanout.texture.width;
case SCANOUT_SURFACE:
@@ -1476,7 +1476,7 @@ int qemu_console_get_height(QemuConsole *con, int fallback)
}
switch (con->scanout.kind) {
case SCANOUT_DMABUF:
- return con->scanout.dmabuf->height;
+ return qemu_dmabuf_get_height(con->scanout.dmabuf);
case SCANOUT_TEXTURE:
return con->scanout.texture.height;
case SCANOUT_SURFACE:
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 49da9ccc83..578b67f62b 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -110,11 +110,14 @@ static void
dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
+ uint32_t width, height;
+
DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
- dbus_display_console_set_size(ddc,
- dmabuf->width,
- dmabuf->height);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+
+ dbus_display_console_set_size(ddc, width, height);
}
static void
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 4a0a5d78f9..5490088043 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -278,29 +278,33 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
g_autoptr(GError) err = NULL;
g_autoptr(GUnixFDList) fd_list = NULL;
+ int fd;
+ uint32_t width, height, stride, fourcc;
+ uint64_t modifier;
+ bool y0_top;
+ fd = qemu_dmabuf_get_fd(dmabuf);
fd_list = g_unix_fd_list_new();
- if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) {
+ if (g_unix_fd_list_append(fd_list, fd, &err) != 0) {
error_report("Failed to setup dmabuf fdlist: %s", err->message);
return;
}
ddl_discard_pending_messages(ddl);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+ stride = qemu_dmabuf_get_stride(dmabuf);
+ fourcc = qemu_dmabuf_get_fourcc(dmabuf);
+ modifier = qemu_dmabuf_get_modifier(dmabuf);
+ y0_top = qemu_dmabuf_get_y0_top(dmabuf);
+
/* FIXME: add missing x/y/w/h support */
qemu_dbus_display1_listener_call_scanout_dmabuf(
- ddl->proxy,
- g_variant_new_handle(0),
- dmabuf->width,
- dmabuf->height,
- dmabuf->stride,
- dmabuf->fourcc,
- dmabuf->modifier,
- dmabuf->y0_top,
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- fd_list,
- NULL, NULL, NULL);
+ ddl->proxy, g_variant_new_handle(0),
+ width, height, stride, fourcc, modifier,
+ y0_top, G_DBUS_CALL_FLAGS_NONE,
+ -1, fd_list, NULL, NULL, NULL);
}
#endif /* GBM */
#endif /* OPENGL */
@@ -438,28 +442,24 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl,
trace_dbus_scanout_texture(tex_id, backing_y_0_top,
backing_width, backing_height, x, y, w, h);
#ifdef CONFIG_GBM
- QemuDmaBuf dmabuf = {
- .width = w,
- .height = h,
- .y0_top = backing_y_0_top,
- .x = x,
- .y = y,
- .backing_width = backing_width,
- .backing_height = backing_height,
- };
+ g_autoptr(QemuDmaBuf) dmabuf = NULL;
+ int fd;
+ uint32_t stride, fourcc;
+ uint64_t modifier;
assert(tex_id);
- dmabuf.fd = egl_get_fd_for_texture(
- tex_id, (EGLint *)&dmabuf.stride,
- (EGLint *)&dmabuf.fourcc,
- &dmabuf.modifier);
- if (dmabuf.fd < 0) {
+ fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc,
+ &modifier);
+ if (fd < 0) {
error_report("%s: failed to get fd for texture", __func__);
return;
}
+ dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width,
+ backing_height, fourcc, modifier, fd,
+ false, backing_y_0_top);
- dbus_scanout_dmabuf(dcl, &dmabuf);
- close(dmabuf.fd);
+ dbus_scanout_dmabuf(dcl, dmabuf);
+ qemu_dmabuf_close(dmabuf);
#endif
#ifdef WIN32
@@ -488,6 +488,7 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
DisplaySurface *ds;
GVariant *v_data = NULL;
egl_fb cursor_fb = EGL_FB_INIT;
+ uint32_t width, height, texture;
if (!dmabuf) {
qemu_dbus_display1_listener_call_mouse_set(
@@ -497,12 +498,16 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
}
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height,
- dmabuf->texture, false);
- ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height);
+
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+
+ egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false);
+ ds = qemu_create_displaysurface(width, height);
egl_fb_read(ds, &cursor_fb);
v_data = g_variant_new_from_data(
diff --git a/ui/dmabuf.c b/ui/dmabuf.c
new file mode 100644
index 0000000000..df7a09703f
--- /dev/null
+++ b/ui/dmabuf.c
@@ -0,0 +1,229 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QemuDmaBuf struct and helpers used for accessing its data
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "ui/dmabuf.h"
+
+struct QemuDmaBuf {
+ int fd;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t fourcc;
+ uint64_t modifier;
+ uint32_t texture;
+ uint32_t x;
+ uint32_t y;
+ uint32_t backing_width;
+ uint32_t backing_height;
+ bool y0_top;
+ void *sync;
+ int fence_fd;
+ bool allow_fences;
+ bool draw_submitted;
+};
+
+QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height,
+ uint32_t stride, uint32_t x,
+ uint32_t y, uint32_t backing_width,
+ uint32_t backing_height, uint32_t fourcc,
+ uint64_t modifier, int32_t dmabuf_fd,
+ bool allow_fences, bool y0_top) {
+ QemuDmaBuf *dmabuf;
+
+ dmabuf = g_new0(QemuDmaBuf, 1);
+
+ dmabuf->width = width;
+ dmabuf->height = height;
+ dmabuf->stride = stride;
+ dmabuf->x = x;
+ dmabuf->y = y;
+ dmabuf->backing_width = backing_width;
+ dmabuf->backing_height = backing_height;
+ dmabuf->fourcc = fourcc;
+ dmabuf->modifier = modifier;
+ dmabuf->fd = dmabuf_fd;
+ dmabuf->allow_fences = allow_fences;
+ dmabuf->y0_top = y0_top;
+ dmabuf->fence_fd = -1;
+
+ return dmabuf;
+}
+
+void qemu_dmabuf_free(QemuDmaBuf *dmabuf)
+{
+ if (dmabuf == NULL) {
+ return;
+ }
+
+ g_free(dmabuf);
+}
+
+int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->fd;
+}
+
+int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ if (dmabuf->fd >= 0) {
+ return dup(dmabuf->fd);
+ } else {
+ return -1;
+ }
+}
+
+void qemu_dmabuf_close(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ if (dmabuf->fd >= 0) {
+ close(dmabuf->fd);
+ dmabuf->fd = -1;
+ }
+}
+
+uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->width;
+}
+
+uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->height;
+}
+
+uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->stride;
+}
+
+uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->fourcc;
+}
+
+uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->modifier;
+}
+
+uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->texture;
+}
+
+uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->x;
+}
+
+uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->y;
+}
+
+uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->backing_width;
+}
+
+uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->backing_height;
+}
+
+bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->y0_top;
+}
+
+void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->sync;
+}
+
+int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->fence_fd;
+}
+
+bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->allow_fences;
+}
+
+bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf)
+{
+ assert(dmabuf != NULL);
+
+ return dmabuf->draw_submitted;
+}
+
+void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture)
+{
+ assert(dmabuf != NULL);
+ dmabuf->texture = texture;
+}
+
+void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd)
+{
+ assert(dmabuf != NULL);
+ dmabuf->fence_fd = fence_fd;
+}
+
+void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync)
+{
+ assert(dmabuf != NULL);
+ dmabuf->sync = sync;
+}
+
+void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted)
+{
+ assert(dmabuf != NULL);
+ dmabuf->draw_submitted = draw_submitted;
+}
+
+void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd)
+{
+ assert(dmabuf != NULL);
+ dmabuf->fd = fd;
+}
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index d5637dadb2..6187249c73 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -85,29 +85,38 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf)
{
+ uint32_t width, height, texture;
+
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- egl_scanout_texture(dcl, dmabuf->texture,
- false, dmabuf->width, dmabuf->height,
- 0, 0, dmabuf->width, dmabuf->height, NULL);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+
+ egl_scanout_texture(dcl, texture, false, width, height, 0, 0,
+ width, height, NULL);
}
static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot,
uint32_t hot_x, uint32_t hot_y)
{
+ uint32_t width, height, texture;
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
if (dmabuf) {
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height,
- dmabuf->texture, false);
+
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+ egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false);
} else {
egl_fb_destroy(&edpy->cursor_fb);
}
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index 3d19dbe382..99b2ebbe23 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -146,10 +146,10 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip)
glViewport(0, 0, dst->width, dst->height);
if (src->dmabuf) {
- x1 = src->dmabuf->x;
- y1 = src->dmabuf->y;
- w = src->dmabuf->width;
- h = src->dmabuf->height;
+ x1 = qemu_dmabuf_get_x(src->dmabuf);
+ y1 = qemu_dmabuf_get_y(src->dmabuf);
+ w = qemu_dmabuf_get_width(src->dmabuf);
+ h = qemu_dmabuf_get_height(src->dmabuf);
}
w = (x1 + w) > src->width ? src->width - x1 : w;
@@ -308,30 +308,33 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
EGLImageKHR image = EGL_NO_IMAGE_KHR;
EGLint attrs[64];
int i = 0;
+ uint64_t modifier;
+ uint32_t texture = qemu_dmabuf_get_texture(dmabuf);
- if (dmabuf->texture != 0) {
+ if (texture != 0) {
return;
}
attrs[i++] = EGL_WIDTH;
- attrs[i++] = dmabuf->backing_width;
+ attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf);
attrs[i++] = EGL_HEIGHT;
- attrs[i++] = dmabuf->backing_height;
+ attrs[i++] = qemu_dmabuf_get_backing_height(dmabuf);
attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
- attrs[i++] = dmabuf->fourcc;
+ attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf);
attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
- attrs[i++] = dmabuf->fd;
+ attrs[i++] = qemu_dmabuf_get_fd(dmabuf);
attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
- attrs[i++] = dmabuf->stride;
+ attrs[i++] = qemu_dmabuf_get_stride(dmabuf);
attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
attrs[i++] = 0;
#ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT
- if (dmabuf->modifier) {
+ modifier = qemu_dmabuf_get_modifier(dmabuf);
+ if (modifier) {
attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
- attrs[i++] = (dmabuf->modifier >> 0) & 0xffffffff;
+ attrs[i++] = (modifier >> 0) & 0xffffffff;
attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
- attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff;
+ attrs[i++] = (modifier >> 32) & 0xffffffff;
}
#endif
attrs[i++] = EGL_NONE;
@@ -345,8 +348,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
return;
}
- glGenTextures(1, &dmabuf->texture);
- glBindTexture(GL_TEXTURE_2D, dmabuf->texture);
+ glGenTextures(1, &texture);
+ qemu_dmabuf_set_texture(dmabuf, texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -356,12 +360,15 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf)
void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf)
{
- if (dmabuf->texture == 0) {
+ uint32_t texture;
+
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (texture == 0) {
return;
}
- glDeleteTextures(1, &dmabuf->texture);
- dmabuf->texture = 0;
+ glDeleteTextures(1, &texture);
+ qemu_dmabuf_set_texture(dmabuf, 0);
}
void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
@@ -375,18 +382,22 @@ void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf)
sync = eglCreateSyncKHR(qemu_egl_display,
EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
if (sync != EGL_NO_SYNC_KHR) {
- dmabuf->sync = sync;
+ qemu_dmabuf_set_sync(dmabuf, sync);
}
}
}
void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
{
- if (dmabuf->sync) {
- dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
- dmabuf->sync);
- eglDestroySyncKHR(qemu_egl_display, dmabuf->sync);
- dmabuf->sync = NULL;
+ void *sync = qemu_dmabuf_get_sync(dmabuf);
+ int fence_fd;
+
+ if (sync) {
+ fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display,
+ sync);
+ qemu_dmabuf_set_fence_fd(dmabuf, fence_fd);
+ eglDestroySyncKHR(qemu_egl_display, sync);
+ qemu_dmabuf_set_sync(dmabuf, NULL);
}
}
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 3af5ac5bcf..9831c10e1b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -68,6 +68,7 @@ void gd_egl_draw(VirtualConsole *vc)
GdkWindow *window;
#ifdef CONFIG_GBM
QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
+ int fence_fd;
#endif
int ww, wh, ws;
@@ -83,10 +84,10 @@ void gd_egl_draw(VirtualConsole *vc)
if (vc->gfx.scanout_mode) {
#ifdef CONFIG_GBM
if (dmabuf) {
- if (!dmabuf->draw_submitted) {
+ if (!qemu_dmabuf_get_draw_submitted(dmabuf)) {
return;
} else {
- dmabuf->draw_submitted = false;
+ qemu_dmabuf_set_draw_submitted(dmabuf, false);
}
}
#endif
@@ -99,8 +100,9 @@ void gd_egl_draw(VirtualConsole *vc)
#ifdef CONFIG_GBM
if (dmabuf) {
egl_dmabuf_create_fence(dmabuf);
- if (dmabuf->fence_fd > 0) {
- qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+ fence_fd = qemu_dmabuf_get_fence_fd(dmabuf);
+ if (fence_fd >= 0) {
+ qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
return;
}
graphic_hw_gl_block(vc->gfx.dcl.con, false);
@@ -149,7 +151,9 @@ void gd_egl_refresh(DisplayChangeListener *dcl)
gd_update_monitor_refresh_rate(
vc, vc->window ? vc->window : vc->gfx.drawing_area);
- if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ if (vc->gfx.guest_fb.dmabuf &&
+ qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
+ gd_egl_draw(vc);
return;
}
@@ -264,22 +268,30 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
{
#ifdef CONFIG_GBM
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ uint32_t x, y, width, height, backing_width, backing_height, texture;
+ bool y0_top;
eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
vc->gfx.esurface, vc->gfx.ectx);
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- gd_egl_scanout_texture(dcl, dmabuf->texture,
- dmabuf->y0_top,
- dmabuf->backing_width, dmabuf->backing_height,
- dmabuf->x, dmabuf->y, dmabuf->width,
- dmabuf->height, NULL);
+ x = qemu_dmabuf_get_x(dmabuf);
+ y = qemu_dmabuf_get_y(dmabuf);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+ backing_width = qemu_dmabuf_get_backing_width(dmabuf);
+ backing_height = qemu_dmabuf_get_backing_height(dmabuf);
+ y0_top = qemu_dmabuf_get_y0_top(dmabuf);
+
+ gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height,
+ x, y, width, height, NULL);
- if (dmabuf->allow_fences) {
+ if (qemu_dmabuf_get_allow_fences(dmabuf)) {
vc->gfx.guest_fb.dmabuf = dmabuf;
}
#endif
@@ -291,15 +303,19 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
{
#ifdef CONFIG_GBM
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ uint32_t backing_width, backing_height, texture;
if (dmabuf) {
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- egl_fb_setup_for_tex(&vc->gfx.cursor_fb,
- dmabuf->backing_width, dmabuf->backing_height,
- dmabuf->texture, false);
+
+ backing_width = qemu_dmabuf_get_backing_width(dmabuf);
+ backing_height = qemu_dmabuf_get_backing_height(dmabuf);
+ egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height,
+ texture, false);
} else {
egl_fb_destroy(&vc->gfx.cursor_fb);
}
@@ -363,9 +379,10 @@ void gd_egl_flush(DisplayChangeListener *dcl,
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
GtkWidget *area = vc->gfx.drawing_area;
- if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ if (vc->gfx.guest_fb.dmabuf &&
+ !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
graphic_hw_gl_block(vc->gfx.dcl.con, true);
- vc->gfx.guest_fb.dmabuf->draw_submitted = true;
+ qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
gtk_egl_set_scanout_mode(vc, true);
gtk_widget_queue_draw_area(area, x, y, w, h);
return;
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 52dcac161e..b628b35451 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -60,10 +60,10 @@ void gd_gl_area_draw(VirtualConsole *vc)
#ifdef CONFIG_GBM
if (dmabuf) {
- if (!dmabuf->draw_submitted) {
+ if (!qemu_dmabuf_get_draw_submitted(dmabuf)) {
return;
} else {
- dmabuf->draw_submitted = false;
+ qemu_dmabuf_set_draw_submitted(dmabuf, false);
}
}
#endif
@@ -85,9 +85,11 @@ void gd_gl_area_draw(VirtualConsole *vc)
glFlush();
#ifdef CONFIG_GBM
if (dmabuf) {
+ int fence_fd;
egl_dmabuf_create_fence(dmabuf);
- if (dmabuf->fence_fd > 0) {
- qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc);
+ fence_fd = qemu_dmabuf_get_fence_fd(dmabuf);
+ if (fence_fd >= 0) {
+ qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc);
return;
}
graphic_hw_gl_block(vc->gfx.dcl.con, false);
@@ -125,7 +127,9 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl)
gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area);
- if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ if (vc->gfx.guest_fb.dmabuf &&
+ qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
+ gd_gl_area_draw(vc);
return;
}
@@ -285,9 +289,10 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
- if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) {
+ if (vc->gfx.guest_fb.dmabuf &&
+ !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) {
graphic_hw_gl_block(vc->gfx.dcl.con, true);
- vc->gfx.guest_fb.dmabuf->draw_submitted = true;
+ qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true);
gtk_gl_area_set_scanout_mode(vc, true);
}
gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
@@ -298,20 +303,29 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
{
#ifdef CONFIG_GBM
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ uint32_t x, y, width, height, backing_width, backing_height, texture;
+ bool y0_top;
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- gd_gl_area_scanout_texture(dcl, dmabuf->texture,
- dmabuf->y0_top,
- dmabuf->backing_width, dmabuf->backing_height,
- dmabuf->x, dmabuf->y, dmabuf->width,
- dmabuf->height, NULL);
+ x = qemu_dmabuf_get_x(dmabuf);
+ y = qemu_dmabuf_get_y(dmabuf);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+ backing_width = qemu_dmabuf_get_backing_width(dmabuf);
+ backing_height = qemu_dmabuf_get_backing_height(dmabuf);
+ y0_top = qemu_dmabuf_get_y0_top(dmabuf);
- if (dmabuf->allow_fences) {
+ gd_gl_area_scanout_texture(dcl, texture, y0_top,
+ backing_width, backing_height,
+ x, y, width, height, NULL);
+
+ if (qemu_dmabuf_get_allow_fences(dmabuf)) {
vc->gfx.guest_fb.dmabuf = dmabuf;
}
#endif
diff --git a/ui/gtk.c b/ui/gtk.c
index 810d7fc796..93b13b7a30 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -596,11 +596,15 @@ void gd_hw_gl_flushed(void *vcon)
{
VirtualConsole *vc = vcon;
QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf;
+ int fence_fd;
- qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL);
- close(dmabuf->fence_fd);
- dmabuf->fence_fd = -1;
- graphic_hw_gl_block(vc->gfx.dcl.con, false);
+ fence_fd = qemu_dmabuf_get_fence_fd(dmabuf);
+ if (fence_fd >= 0) {
+ qemu_set_fd_handler(fence_fd, NULL, NULL, NULL);
+ close(fence_fd);
+ qemu_dmabuf_set_fence_fd(dmabuf, -1);
+ graphic_hw_gl_block(vc->gfx.dcl.con, false);
+ }
}
/** DisplayState Callbacks (opengl version) **/
@@ -887,7 +891,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
int x, y;
int mx, my;
int fbh, fbw;
- int ww, wh, ws;
+ int ww, wh;
if (!vc->gfx.ds) {
return TRUE;
@@ -895,11 +899,15 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
-
ww = gtk_widget_get_allocated_width(widget);
wh = gtk_widget_get_allocated_height(widget);
- ws = gtk_widget_get_scale_factor(widget);
+ /*
+ * `widget` may not have the same size with the frame buffer.
+ * In such cases, some paddings are needed around the `vc`.
+ * To achieve that, `vc` will be displayed at (mx, my)
+ * so that it is displayed at the center of the widget.
+ */
mx = my = 0;
if (ww > fbw) {
mx = (ww - fbw) / 2;
@@ -908,8 +916,14 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
my = (wh - fbh) / 2;
}
- x = (motion->x - mx) / vc->gfx.scale_x * ws;
- y = (motion->y - my) / vc->gfx.scale_y * ws;
+ /*
+ * `motion` is reported in `widget` coordinates
+ * so translating it to the coordinates in `vc`.
+ */
+ x = (motion->x - mx) / vc->gfx.scale_x;
+ y = (motion->y - my) / vc->gfx.scale_y;
+
+ trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y);
if (qemu_input_is_absolute(vc->gfx.dcl.con)) {
if (x < 0 || y < 0 ||
diff --git a/ui/meson.build b/ui/meson.build
index a5ce22a678..5d89986b0e 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -7,6 +7,7 @@ system_ss.add(files(
'clipboard.c',
'console.c',
'cursor.c',
+ 'dmabuf.c',
'input-keymap.c',
'input-legacy.c',
'input-barrier.c',
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 4971963f00..0a0eb5a42d 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -874,6 +874,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
#endif
SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1");
+ SDL_EnableScreenSaver();
memset(&info, 0, sizeof(info));
SDL_VERSION(&info.version);
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 6eb98a5a5c..8a8472d029 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -976,6 +976,7 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
uint32_t hot_x, uint32_t hot_y)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+ uint32_t width, height, texture;
ssd->have_hot = have_hot;
ssd->hot_x = hot_x;
@@ -984,11 +985,13 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl,
trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot);
if (dmabuf) {
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
- egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height,
- dmabuf->texture, false);
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+ egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false);
} else {
egl_fb_destroy(&ssd->cursor_fb);
}
@@ -1026,6 +1029,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
bool y_0_top = false; /* FIXME */
uint64_t cookie;
int fd;
+ uint32_t width, height, texture;
if (!ssd->have_scanout) {
return;
@@ -1042,41 +1046,45 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
if (ssd->guest_dmabuf_refresh) {
QemuDmaBuf *dmabuf = ssd->guest_dmabuf;
+ width = qemu_dmabuf_get_width(dmabuf);
+ height = qemu_dmabuf_get_height(dmabuf);
+
if (render_cursor) {
egl_dmabuf_import_texture(dmabuf);
- if (!dmabuf->texture) {
+ texture = qemu_dmabuf_get_texture(dmabuf);
+ if (!texture) {
return;
}
/* source framebuffer */
- egl_fb_setup_for_tex(&ssd->guest_fb,
- dmabuf->width, dmabuf->height,
- dmabuf->texture, false);
+ egl_fb_setup_for_tex(&ssd->guest_fb, width, height,
+ texture, false);
/* dest framebuffer */
- if (ssd->blit_fb.width != dmabuf->width ||
- ssd->blit_fb.height != dmabuf->height) {
- trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width,
- dmabuf->height);
+ if (ssd->blit_fb.width != width ||
+ ssd->blit_fb.height != height) {
+ trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width,
+ height);
egl_fb_destroy(&ssd->blit_fb);
egl_fb_setup_new_tex(&ssd->blit_fb,
- dmabuf->width, dmabuf->height);
+ width, height);
fd = egl_get_fd_for_texture(ssd->blit_fb.texture,
&stride, &fourcc, NULL);
- spice_qxl_gl_scanout(&ssd->qxl, fd,
- dmabuf->width, dmabuf->height,
+ spice_qxl_gl_scanout(&ssd->qxl, fd, width, height,
stride, fourcc, false);
}
} else {
- trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id,
- dmabuf->width, dmabuf->height);
+ stride = qemu_dmabuf_get_stride(dmabuf);
+ fourcc = qemu_dmabuf_get_fourcc(dmabuf);
+ y_0_top = qemu_dmabuf_get_y0_top(dmabuf);
+ fd = qemu_dmabuf_dup_fd(dmabuf);
+
+ trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height);
/* note: spice server will close the fd, so hand over a dup */
- spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd),
- dmabuf->width, dmabuf->height,
- dmabuf->stride, dmabuf->fourcc,
- dmabuf->y0_top);
+ spice_qxl_gl_scanout(&ssd->qxl, fd, width, height,
+ stride, fourcc, y_0_top);
}
- qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height);
+ qemu_spice_gl_monitor_config(ssd, 0, 0, width, height);
ssd->guest_dmabuf_refresh = false;
}
diff --git a/ui/trace-events b/ui/trace-events
index e6a2894303..69ff22955d 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -28,6 +28,7 @@ gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s"
gd_keymap_windowing(const char *name) "backend=%s"
gd_gl_area_create_context(void *ctx, int major, int minor) "ctx=%p, major=%d, minor=%d"
gd_gl_area_destroy_context(void *ctx, void *current_ctx) "ctx=%p, current_ctx=%p"
+gd_motion_event(int ww, int wh, int ws, int x, int y) "ww=%d, wh=%d, ws=%d, x=%d, y=%d"
# vnc-auth-sasl.c
# vnc-auth-vencrypt.c
diff --git a/ui/vnc.c b/ui/vnc.c
index b3fd78022b..dd530f04e5 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3734,11 +3734,6 @@ static int vnc_display_get_address(const char *addrstr,
addr->type = SOCKET_ADDRESS_TYPE_UNIX;
addr->u.q_unix.path = g_strdup(addrstr + 5);
- if (websocket) {
- error_setg(errp, "UNIX sockets not supported with websock");
- goto cleanup;
- }
-
if (to) {
error_setg(errp, "Port range not support with UNIX socket");
goto cleanup;
diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 3e6a5dfd63..74864f7b78 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -26,265 +26,290 @@
#include "qemu/bswap.h"
#include "host/cpuinfo.h"
-static bool
-buffer_zero_int(const void *buf, size_t len)
+typedef bool (*biz_accel_fn)(const void *, size_t);
+
+static bool buffer_is_zero_int_lt256(const void *buf, size_t len)
{
- if (unlikely(len < 8)) {
- /* For a very small buffer, simply accumulate all the bytes. */
- const unsigned char *p = buf;
- const unsigned char *e = buf + len;
- unsigned char t = 0;
-
- do {
- t |= *p++;
- } while (p < e);
-
- return t == 0;
- } else {
- /* Otherwise, use the unaligned memory access functions to
- handle the beginning and end of the buffer, with a couple
- of loops handling the middle aligned section. */
- uint64_t t = ldq_he_p(buf);
- const uint64_t *p = (uint64_t *)(((uintptr_t)buf + 8) & -8);
- const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8);
-
- for (; p + 8 <= e; p += 8) {
- __builtin_prefetch(p + 8);
- if (t) {
- return false;
- }
- t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
- }
- while (p < e) {
- t |= *p++;
- }
- t |= ldq_he_p(buf + len - 8);
+ uint64_t t;
+ const uint64_t *p, *e;
- return t == 0;
+ /*
+ * Use unaligned memory access functions to handle
+ * the beginning and end of the buffer.
+ */
+ if (unlikely(len <= 8)) {
+ return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0;
}
-}
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__)
-#include <immintrin.h>
+ t = ldq_he_p(buf) | ldq_he_p(buf + len - 8);
+ p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8);
+ e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8);
-/* Note that each of these vectorized functions require len >= 64. */
+ /* Read 0 to 31 aligned words from the middle. */
+ while (p < e) {
+ t |= *p++;
+ }
+ return t == 0;
+}
-static bool __attribute__((target("sse2")))
-buffer_zero_sse2(const void *buf, size_t len)
+static bool buffer_is_zero_int_ge256(const void *buf, size_t len)
{
- __m128i t = _mm_loadu_si128(buf);
- __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16);
- __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16);
- __m128i zero = _mm_setzero_si128();
-
- /* Loop over 16-byte aligned blocks of 64. */
- while (likely(p <= e)) {
- __builtin_prefetch(p);
- t = _mm_cmpeq_epi8(t, zero);
- if (unlikely(_mm_movemask_epi8(t) != 0xFFFF)) {
+ /*
+ * Use unaligned memory access functions to handle
+ * the beginning and end of the buffer.
+ */
+ uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8);
+ const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8);
+ const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8);
+
+ /* Collect a partial block at the tail end. */
+ t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1];
+
+ /*
+ * Loop over 64 byte blocks.
+ * With the head and tail removed, e - p >= 30,
+ * so the loop must iterate at least 3 times.
+ */
+ do {
+ if (t) {
return false;
}
- t = p[-4] | p[-3] | p[-2] | p[-1];
- p += 4;
- }
+ t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
+ p += 8;
+ } while (p < e - 7);
- /* Finish the aligned tail. */
- t |= e[-3];
- t |= e[-2];
- t |= e[-1];
+ return t == 0;
+}
- /* Finish the unaligned tail. */
- t |= _mm_loadu_si128(buf + len - 16);
+#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__)
+#include <immintrin.h>
- return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF;
-}
+/* Helper for preventing the compiler from reassociating
+ chains of binary vector operations. */
+#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1))
-#ifdef CONFIG_AVX2_OPT
-static bool __attribute__((target("sse4")))
-buffer_zero_sse4(const void *buf, size_t len)
+/* Note that these vectorized functions may assume len >= 256. */
+
+static bool __attribute__((target("sse2")))
+buffer_zero_sse2(const void *buf, size_t len)
{
- __m128i t = _mm_loadu_si128(buf);
- __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16);
- __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16);
-
- /* Loop over 16-byte aligned blocks of 64. */
- while (likely(p <= e)) {
- __builtin_prefetch(p);
- if (unlikely(!_mm_testz_si128(t, t))) {
+ /* Unaligned loads at head/tail. */
+ __m128i v = *(__m128i_u *)(buf);
+ __m128i w = *(__m128i_u *)(buf + len - 16);
+ /* Align head/tail to 16-byte boundaries. */
+ const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16);
+ const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16);
+ __m128i zero = { 0 };
+
+ /* Collect a partial block at tail end. */
+ v |= e[-1]; w |= e[-2];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-3]; w |= e[-4];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-5]; w |= e[-6];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-7]; v |= w;
+
+ /*
+ * Loop over complete 128-byte blocks.
+ * With the head and tail removed, e - p >= 14, so the loop
+ * must iterate at least once.
+ */
+ do {
+ v = _mm_cmpeq_epi8(v, zero);
+ if (unlikely(_mm_movemask_epi8(v) != 0xFFFF)) {
return false;
}
- t = p[-4] | p[-3] | p[-2] | p[-1];
- p += 4;
- }
-
- /* Finish the aligned tail. */
- t |= e[-3];
- t |= e[-2];
- t |= e[-1];
-
- /* Finish the unaligned tail. */
- t |= _mm_loadu_si128(buf + len - 16);
-
- return _mm_testz_si128(t, t);
+ v = p[0]; w = p[1];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[2]; w |= p[3];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[4]; w |= p[5];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[6]; w |= p[7];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= w;
+ p += 8;
+ } while (p < e - 7);
+
+ return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0xFFFF;
}
+#ifdef CONFIG_AVX2_OPT
static bool __attribute__((target("avx2")))
buffer_zero_avx2(const void *buf, size_t len)
{
- /* Begin with an unaligned head of 32 bytes. */
- __m256i t = _mm256_loadu_si256(buf);
- __m256i *p = (__m256i *)(((uintptr_t)buf + 5 * 32) & -32);
- __m256i *e = (__m256i *)(((uintptr_t)buf + len) & -32);
-
- /* Loop over 32-byte aligned blocks of 128. */
- while (p <= e) {
- __builtin_prefetch(p);
- if (unlikely(!_mm256_testz_si256(t, t))) {
+ /* Unaligned loads at head/tail. */
+ __m256i v = *(__m256i_u *)(buf);
+ __m256i w = *(__m256i_u *)(buf + len - 32);
+ /* Align head/tail to 32-byte boundaries. */
+ const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32);
+ const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32);
+ __m256i zero = { 0 };
+
+ /* Collect a partial block at tail end. */
+ v |= e[-1]; w |= e[-2];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-3]; w |= e[-4];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-5]; w |= e[-6];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= e[-7]; v |= w;
+
+ /* Loop over complete 256-byte blocks. */
+ for (; p < e - 7; p += 8) {
+ /* PTEST is not profitable here. */
+ v = _mm256_cmpeq_epi8(v, zero);
+ if (unlikely(_mm256_movemask_epi8(v) != 0xFFFFFFFF)) {
return false;
}
- t = p[-4] | p[-3] | p[-2] | p[-1];
- p += 4;
- } ;
-
- /* Finish the last block of 128 unaligned. */
- t |= _mm256_loadu_si256(buf + len - 4 * 32);
- t |= _mm256_loadu_si256(buf + len - 3 * 32);
- t |= _mm256_loadu_si256(buf + len - 2 * 32);
- t |= _mm256_loadu_si256(buf + len - 1 * 32);
+ v = p[0]; w = p[1];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[2]; w |= p[3];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[4]; w |= p[5];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= p[6]; w |= p[7];
+ SSE_REASSOC_BARRIER(v, w);
+ v |= w;
+ }
- return _mm256_testz_si256(t, t);
+ return _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, zero)) == 0xFFFFFFFF;
}
#endif /* CONFIG_AVX2_OPT */
-#ifdef CONFIG_AVX512F_OPT
-static bool __attribute__((target("avx512f")))
-buffer_zero_avx512(const void *buf, size_t len)
+static biz_accel_fn const accel_table[] = {
+ buffer_is_zero_int_ge256,
+ buffer_zero_sse2,
+#ifdef CONFIG_AVX2_OPT
+ buffer_zero_avx2,
+#endif
+};
+
+static unsigned best_accel(void)
{
- /* Begin with an unaligned head of 64 bytes. */
- __m512i t = _mm512_loadu_si512(buf);
- __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64);
- __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64);
-
- /* Loop over 64-byte aligned blocks of 256. */
- while (p <= e) {
- __builtin_prefetch(p);
- if (unlikely(_mm512_test_epi64_mask(t, t))) {
- return false;
- }
- t = p[-4] | p[-3] | p[-2] | p[-1];
- p += 4;
+ unsigned info = cpuinfo_init();
+
+#ifdef CONFIG_AVX2_OPT
+ if (info & CPUINFO_AVX2) {
+ return 2;
}
+#endif
+ return info & CPUINFO_SSE2 ? 1 : 0;
+}
- t |= _mm512_loadu_si512(buf + len - 4 * 64);
- t |= _mm512_loadu_si512(buf + len - 3 * 64);
- t |= _mm512_loadu_si512(buf + len - 2 * 64);
- t |= _mm512_loadu_si512(buf + len - 1 * 64);
+#elif defined(__aarch64__) && defined(__ARM_NEON)
+#include <arm_neon.h>
- return !_mm512_test_epi64_mask(t, t);
+/*
+ * Helper for preventing the compiler from reassociating
+ * chains of binary vector operations.
+ */
+#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1))
+
+static bool buffer_is_zero_simd(const void *buf, size_t len)
+{
+ uint32x4_t t0, t1, t2, t3;
+
+ /* Align head/tail to 16-byte boundaries. */
+ const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16);
+ const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16);
+
+ /* Unaligned loads at head/tail. */
+ t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16);
+
+ /* Collect a partial block at tail end. */
+ t1 = e[-7] | e[-6];
+ t2 = e[-5] | e[-4];
+ t3 = e[-3] | e[-2];
+ t0 |= e[-1];
+ REASSOC_BARRIER(t0, t1);
+ REASSOC_BARRIER(t2, t3);
+ t0 |= t1;
+ t2 |= t3;
+ REASSOC_BARRIER(t0, t2);
+ t0 |= t2;
+ /*
+ * Loop over complete 128-byte blocks.
+ * With the head and tail removed, e - p >= 14, so the loop
+ * must iterate at least once.
+ */
+ do {
+ /*
+ * Reduce via UMAXV. Whatever the actual result,
+ * it will only be zero if all input bytes are zero.
+ */
+ if (unlikely(vmaxvq_u32(t0) != 0)) {
+ return false;
+ }
+
+ t0 = p[0] | p[1];
+ t1 = p[2] | p[3];
+ t2 = p[4] | p[5];
+ t3 = p[6] | p[7];
+ REASSOC_BARRIER(t0, t1);
+ REASSOC_BARRIER(t2, t3);
+ t0 |= t1;
+ t2 |= t3;
+ REASSOC_BARRIER(t0, t2);
+ t0 |= t2;
+ p += 8;
+ } while (p < e - 7);
+
+ return vmaxvq_u32(t0) == 0;
}
-#endif /* CONFIG_AVX512F_OPT */
-/*
- * Make sure that these variables are appropriately initialized when
- * SSE2 is enabled on the compiler command-line, but the compiler is
- * too old to support CONFIG_AVX2_OPT.
- */
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT)
-# define INIT_USED 0
-# define INIT_LENGTH 0
-# define INIT_ACCEL buffer_zero_int
+#define best_accel() 1
+static biz_accel_fn const accel_table[] = {
+ buffer_is_zero_int_ge256,
+ buffer_is_zero_simd,
+};
#else
-# ifndef __SSE2__
-# error "ISA selection confusion"
-# endif
-# define INIT_USED CPUINFO_SSE2
-# define INIT_LENGTH 64
-# define INIT_ACCEL buffer_zero_sse2
+#define best_accel() 0
+static biz_accel_fn const accel_table[1] = {
+ buffer_is_zero_int_ge256
+};
#endif
-static unsigned used_accel = INIT_USED;
-static unsigned length_to_accel = INIT_LENGTH;
-static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL;
+static biz_accel_fn buffer_is_zero_accel;
+static unsigned accel_index;
-static unsigned __attribute__((noinline))
-select_accel_cpuinfo(unsigned info)
+bool buffer_is_zero_ool(const void *buf, size_t len)
{
- /* Array is sorted in order of algorithm preference. */
- static const struct {
- unsigned bit;
- unsigned len;
- bool (*fn)(const void *, size_t);
- } all[] = {
-#ifdef CONFIG_AVX512F_OPT
- { CPUINFO_AVX512F, 256, buffer_zero_avx512 },
-#endif
-#ifdef CONFIG_AVX2_OPT
- { CPUINFO_AVX2, 128, buffer_zero_avx2 },
- { CPUINFO_SSE4, 64, buffer_zero_sse4 },
-#endif
- { CPUINFO_SSE2, 64, buffer_zero_sse2 },
- { CPUINFO_ALWAYS, 0, buffer_zero_int },
- };
-
- for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) {
- if (info & all[i].bit) {
- length_to_accel = all[i].len;
- buffer_accel = all[i].fn;
- return all[i].bit;
- }
+ if (unlikely(len == 0)) {
+ return true;
+ }
+ if (!buffer_is_zero_sample3(buf, len)) {
+ return false;
+ }
+ /* All bytes are covered for any len <= 3. */
+ if (unlikely(len <= 3)) {
+ return true;
}
- return 0;
-}
-
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT)
-static void __attribute__((constructor)) init_accel(void)
-{
- used_accel = select_accel_cpuinfo(cpuinfo_init());
-}
-#endif /* CONFIG_AVX2_OPT */
-bool test_buffer_is_zero_next_accel(void)
-{
- /*
- * Accumulate the accelerators that we've already tested, and
- * remove them from the set to test this round. We'll get back
- * a zero from select_accel_cpuinfo when there are no more.
- */
- unsigned used = select_accel_cpuinfo(cpuinfo & ~used_accel);
- used_accel |= used;
- return used;
+ if (likely(len >= 256)) {
+ return buffer_is_zero_accel(buf, len);
+ }
+ return buffer_is_zero_int_lt256(buf, len);
}
-static bool select_accel_fn(const void *buf, size_t len)
+bool buffer_is_zero_ge256(const void *buf, size_t len)
{
- if (likely(len >= length_to_accel)) {
- return buffer_accel(buf, len);
- }
- return buffer_zero_int(buf, len);
+ return buffer_is_zero_accel(buf, len);
}
-#else
-#define select_accel_fn buffer_zero_int
bool test_buffer_is_zero_next_accel(void)
{
+ if (accel_index != 0) {
+ buffer_is_zero_accel = accel_table[--accel_index];
+ return true;
+ }
return false;
}
-#endif
-/*
- * Checks if a buffer is all zeroes
- */
-bool buffer_is_zero(const void *buf, size_t len)
+static void __attribute__((constructor)) init_accel(void)
{
- if (unlikely(len == 0)) {
- return true;
- }
-
- /* Fetch the beginning of the buffer while we select the accelerator. */
- __builtin_prefetch(buf);
-
- /* Use an optimized zero check if possible. Note that this also
- includes a check for an unrolled loop over 64-bit integers. */
- return select_accel_fn(buf, len);
+ accel_index = best_accel();
+ buffer_is_zero_accel = accel_table[accel_index];
}
diff --git a/util/error-report.c b/util/error-report.c
index 6e44a55732..1b17c11de1 100644
--- a/util/error-report.c
+++ b/util/error-report.c
@@ -172,18 +172,8 @@ static void print_loc(void)
static char *
real_time_iso8601(void)
{
-#if GLIB_CHECK_VERSION(2,62,0)
g_autoptr(GDateTime) dt = g_date_time_new_now_utc();
- /* ignore deprecation warning, since GLIB_VERSION_MAX_ALLOWED is 2.56 */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return g_date_time_format_iso8601(dt);
-#pragma GCC diagnostic pop
-#else
- GTimeVal tv;
- g_get_current_time(&tv);
- return g_time_val_to_iso8601(&tv);
-#endif
}
/*
diff --git a/util/log.c b/util/log.c
index d36c98da0b..6219819855 100644
--- a/util/log.c
+++ b/util/log.c
@@ -466,6 +466,10 @@ const QEMULogItem qemu_log_items[] = {
"show micro ops after optimization" },
{ CPU_LOG_TB_OP_IND, "op_ind",
"show micro ops before indirect lowering" },
+#ifdef CONFIG_PLUGIN
+ { LOG_TB_OP_PLUGIN, "op_plugin",
+ "show micro ops before plugin injection" },
+#endif
{ CPU_LOG_INT, "int",
"show interrupts/exceptions in short format" },
{ CPU_LOG_EXEC, "exec",
diff --git a/util/meson.build b/util/meson.build
index 2ad57b10ba..72b505df11 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -93,7 +93,7 @@ if have_block
util_ss.add(files('hbitmap.c'))
util_ss.add(files('hexdump.c'))
util_ss.add(files('iova-tree.c'))
- util_ss.add(files('iov.c', 'uri.c'))
+ util_ss.add(files('iov.c'))
util_ss.add(files('nvdimm-utils.c'))
util_ss.add(files('block-helpers.c'))
util_ss.add(files('qemu-coroutine-sleep.c'))
diff --git a/util/uri.c b/util/uri.c
deleted file mode 100644
index 573174bf47..0000000000
--- a/util/uri.c
+++ /dev/null
@@ -1,1466 +0,0 @@
-/**
- * uri.c: set of generic URI related routines
- *
- * Reference: RFCs 3986, 2732 and 2373
- *
- * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
- *
- * 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
- * DANIEL VEILLARD 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.
- *
- * Except as contained in this notice, the name of Daniel Veillard shall not
- * be used in advertising or otherwise to promote the sale, use or other
- * dealings in this Software without prior written authorization from him.
- *
- * daniel@veillard.com
- *
- **
- *
- * Copyright (C) 2007, 2009-2010 Red Hat, Inc.
- *
- * 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.1 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 <https://www.gnu.org/licenses/>.
- *
- * Authors:
- * Richard W.M. Jones <rjones@redhat.com>
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu/cutils.h"
-
-#include "qemu/uri.h"
-
-static void uri_clean(URI *uri);
-
-/*
- * Old rule from 2396 used in legacy handling code
- * alpha = lowalpha | upalpha
- */
-#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x))
-
-/*
- * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" |
- * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" |
- * "u" | "v" | "w" | "x" | "y" | "z"
- */
-
-#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z'))
-
-/*
- * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" |
- * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" |
- * "U" | "V" | "W" | "X" | "Y" | "Z"
- */
-#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z'))
-
-#ifdef IS_DIGIT
-#undef IS_DIGIT
-#endif
-/*
- * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
- */
-#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9'))
-
-/*
- * alphanum = alpha | digit
- */
-
-#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x))
-
-/*
- * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
- */
-
-#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \
- ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \
- ((x) == '(') || ((x) == ')'))
-
-/*
- * unwise = "{" | "}" | "|" | "\" | "^" | "`"
- */
-
-#define IS_UNWISE(p) \
- (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \
- ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \
- ((*(p) == ']')) || ((*(p) == '`')))
-/*
- * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
- * "[" | "]"
- */
-
-#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \
- ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \
- ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
- ((x) == ']'))
-
-/*
- * unreserved = alphanum | mark
- */
-
-#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x))
-
-/*
- * Skip to next pointer char, handle escaped sequences
- */
-
-#define NEXT(p) ((*p == '%') ? p += 3 : p++)
-
-/*
- * Productions from the spec.
- *
- * authority = server | reg_name
- * reg_name = 1*( unreserved | escaped | "$" | "," |
- * ";" | ":" | "@" | "&" | "=" | "+" )
- *
- * path = [ abs_path | opaque_part ]
- */
-
-/************************************************************************
- * *
- * RFC 3986 parser *
- * *
- ************************************************************************/
-
-#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9'))
-#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \
- ((*(p) >= 'A') && (*(p) <= 'Z')))
-#define ISA_HEXDIG(p) \
- (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \
- ((*(p) >= 'A') && (*(p) <= 'F')))
-
-/*
- * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
- * / "*" / "+" / "," / ";" / "="
- */
-#define ISA_SUB_DELIM(p) \
- (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \
- ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \
- ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \
- ((*(p) == '=')) || ((*(p) == '\'')))
-
-/*
- * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
- */
-#define ISA_UNRESERVED(p) \
- ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \
- ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~')))
-
-/*
- * pct-encoded = "%" HEXDIG HEXDIG
- */
-#define ISA_PCT_ENCODED(p) \
- ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2)))
-
-/*
- * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
- */
-#define ISA_PCHAR(p) \
- (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \
- ((*(p) == ':')) || ((*(p) == '@')))
-
-/**
- * rfc3986_parse_scheme:
- * @uri: pointer to an URI structure
- * @str: pointer to the string to analyze
- *
- * Parse an URI scheme
- *
- * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_scheme(URI *uri, const char **str)
-{
- const char *cur;
-
- if (str == NULL) {
- return -1;
- }
-
- cur = *str;
- if (!ISA_ALPHA(cur)) {
- return 2;
- }
- cur++;
- while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') ||
- (*cur == '.')) {
- cur++;
- }
- if (uri != NULL) {
- g_free(uri->scheme);
- uri->scheme = g_strndup(*str, cur - *str);
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_fragment:
- * @uri: pointer to an URI structure
- * @str: pointer to the string to analyze
- *
- * Parse the query part of an URI
- *
- * fragment = *( pchar / "/" / "?" )
- * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']'
- * in the fragment identifier but this is used very broadly for
- * xpointer scheme selection, so we are allowing it here to not break
- * for example all the DocBook processing chains.
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_fragment(URI *uri, const char **str)
-{
- const char *cur;
-
- if (str == NULL) {
- return -1;
- }
-
- cur = *str;
-
- while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
- (*cur == '[') || (*cur == ']') ||
- ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) {
- NEXT(cur);
- }
- if (uri != NULL) {
- g_free(uri->fragment);
- if (uri->cleanup & 2) {
- uri->fragment = g_strndup(*str, cur - *str);
- } else {
- uri->fragment = g_uri_unescape_segment(*str, cur, NULL);
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_query:
- * @uri: pointer to an URI structure
- * @str: pointer to the string to analyze
- *
- * Parse the query part of an URI
- *
- * query = *uric
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_query(URI *uri, const char **str)
-{
- const char *cur;
-
- if (str == NULL) {
- return -1;
- }
-
- cur = *str;
-
- while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
- ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) {
- NEXT(cur);
- }
- if (uri != NULL) {
- g_free(uri->query);
- uri->query = g_strndup(*str, cur - *str);
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_port:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse a port part and fills in the appropriate fields
- * of the @uri structure
- *
- * port = *DIGIT
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_port(URI *uri, const char **str)
-{
- const char *cur = *str;
- int port = 0;
-
- if (ISA_DIGIT(cur)) {
- while (ISA_DIGIT(cur)) {
- port = port * 10 + (*cur - '0');
- if (port > 65535) {
- return 1;
- }
- cur++;
- }
- if (uri) {
- uri->port = port;
- }
- *str = cur;
- return 0;
- }
- return 1;
-}
-
-/**
- * rfc3986_parse_user_info:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse a user information part and fill in the appropriate fields
- * of the @uri structure
- *
- * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_user_info(URI *uri, const char **str)
-{
- const char *cur;
-
- cur = *str;
- while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) ||
- (*cur == ':')) {
- NEXT(cur);
- }
- if (*cur == '@') {
- if (uri != NULL) {
- g_free(uri->user);
- if (uri->cleanup & 2) {
- uri->user = g_strndup(*str, cur - *str);
- } else {
- uri->user = g_uri_unescape_segment(*str, cur, NULL);
- }
- }
- *str = cur;
- return 0;
- }
- return 1;
-}
-
-/**
- * rfc3986_parse_dec_octet:
- * @str: the string to analyze
- *
- * dec-octet = DIGIT ; 0-9
- * / %x31-39 DIGIT ; 10-99
- * / "1" 2DIGIT ; 100-199
- * / "2" %x30-34 DIGIT ; 200-249
- * / "25" %x30-35 ; 250-255
- *
- * Skip a dec-octet.
- *
- * Returns 0 if found and skipped, 1 otherwise
- */
-static int rfc3986_parse_dec_octet(const char **str)
-{
- const char *cur = *str;
-
- if (!(ISA_DIGIT(cur))) {
- return 1;
- }
- if (!ISA_DIGIT(cur + 1)) {
- cur++;
- } else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur + 2))) {
- cur += 2;
- } else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) {
- cur += 3;
- } else if ((*cur == '2') && (*(cur + 1) >= '0') && (*(cur + 1) <= '4') &&
- (ISA_DIGIT(cur + 2))) {
- cur += 3;
- } else if ((*cur == '2') && (*(cur + 1) == '5') && (*(cur + 2) >= '0') &&
- (*(cur + 1) <= '5')) {
- cur += 3;
- } else {
- return 1;
- }
- *str = cur;
- return 0;
-}
-/**
- * rfc3986_parse_host:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an host part and fills in the appropriate fields
- * of the @uri structure
- *
- * host = IP-literal / IPv4address / reg-name
- * IP-literal = "[" ( IPv6address / IPvFuture ) "]"
- * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
- * reg-name = *( unreserved / pct-encoded / sub-delims )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_host(URI *uri, const char **str)
-{
- const char *cur = *str;
- const char *host;
-
- host = cur;
- /*
- * IPv6 and future addressing scheme are enclosed between brackets
- */
- if (*cur == '[') {
- cur++;
- while ((*cur != ']') && (*cur != 0)) {
- cur++;
- }
- if (*cur != ']') {
- return 1;
- }
- cur++;
- goto found;
- }
- /*
- * try to parse an IPv4
- */
- if (ISA_DIGIT(cur)) {
- if (rfc3986_parse_dec_octet(&cur) != 0) {
- goto not_ipv4;
- }
- if (*cur != '.') {
- goto not_ipv4;
- }
- cur++;
- if (rfc3986_parse_dec_octet(&cur) != 0) {
- goto not_ipv4;
- }
- if (*cur != '.') {
- goto not_ipv4;
- }
- if (rfc3986_parse_dec_octet(&cur) != 0) {
- goto not_ipv4;
- }
- if (*cur != '.') {
- goto not_ipv4;
- }
- if (rfc3986_parse_dec_octet(&cur) != 0) {
- goto not_ipv4;
- }
- goto found;
- not_ipv4:
- cur = *str;
- }
- /*
- * then this should be a hostname which can be empty
- */
- while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) {
- NEXT(cur);
- }
-found:
- if (uri != NULL) {
- g_free(uri->authority);
- uri->authority = NULL;
- g_free(uri->server);
- if (cur != host) {
- if (uri->cleanup & 2) {
- uri->server = g_strndup(host, cur - host);
- } else {
- uri->server = g_uri_unescape_segment(host, cur, NULL);
- }
- } else {
- uri->server = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_authority:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an authority part and fills in the appropriate fields
- * of the @uri structure
- *
- * authority = [ userinfo "@" ] host [ ":" port ]
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_authority(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
- /*
- * try to parse a userinfo and check for the trailing @
- */
- ret = rfc3986_parse_user_info(uri, &cur);
- if ((ret != 0) || (*cur != '@')) {
- cur = *str;
- } else {
- cur++;
- }
- ret = rfc3986_parse_host(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- if (*cur == ':') {
- cur++;
- ret = rfc3986_parse_port(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_segment:
- * @str: the string to analyze
- * @forbid: an optional forbidden character
- * @empty: allow an empty segment
- *
- * Parse a segment and fills in the appropriate fields
- * of the @uri structure
- *
- * segment = *pchar
- * segment-nz = 1*pchar
- * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
- * ; non-zero-length segment without any colon ":"
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_segment(const char **str, char forbid, int empty)
-{
- const char *cur;
-
- cur = *str;
- if (!ISA_PCHAR(cur)) {
- if (empty) {
- return 0;
- }
- return 1;
- }
- while (ISA_PCHAR(cur) && (*cur != forbid)) {
- NEXT(cur);
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_path_ab_empty:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an path absolute or empty and fills in the appropriate fields
- * of the @uri structure
- *
- * path-abempty = *( "/" segment )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_path_ab_empty(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
-
- while (*cur == '/') {
- cur++;
- ret = rfc3986_parse_segment(&cur, 0, 1);
- if (ret != 0) {
- return ret;
- }
- }
- if (uri != NULL) {
- g_free(uri->path);
- if (*str != cur) {
- if (uri->cleanup & 2) {
- uri->path = g_strndup(*str, cur - *str);
- } else {
- uri->path = g_uri_unescape_segment(*str, cur, NULL);
- }
- } else {
- uri->path = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_path_absolute:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an path absolute and fills in the appropriate fields
- * of the @uri structure
- *
- * path-absolute = "/" [ segment-nz *( "/" segment ) ]
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_path_absolute(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
-
- if (*cur != '/') {
- return 1;
- }
- cur++;
- ret = rfc3986_parse_segment(&cur, 0, 0);
- if (ret == 0) {
- while (*cur == '/') {
- cur++;
- ret = rfc3986_parse_segment(&cur, 0, 1);
- if (ret != 0) {
- return ret;
- }
- }
- }
- if (uri != NULL) {
- g_free(uri->path);
- if (cur != *str) {
- if (uri->cleanup & 2) {
- uri->path = g_strndup(*str, cur - *str);
- } else {
- uri->path = g_uri_unescape_segment(*str, cur, NULL);
- }
- } else {
- uri->path = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_path_rootless:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an path without root and fills in the appropriate fields
- * of the @uri structure
- *
- * path-rootless = segment-nz *( "/" segment )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_path_rootless(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
-
- ret = rfc3986_parse_segment(&cur, 0, 0);
- if (ret != 0) {
- return ret;
- }
- while (*cur == '/') {
- cur++;
- ret = rfc3986_parse_segment(&cur, 0, 1);
- if (ret != 0) {
- return ret;
- }
- }
- if (uri != NULL) {
- g_free(uri->path);
- if (cur != *str) {
- if (uri->cleanup & 2) {
- uri->path = g_strndup(*str, cur - *str);
- } else {
- uri->path = g_uri_unescape_segment(*str, cur, NULL);
- }
- } else {
- uri->path = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_path_no_scheme:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an path which is not a scheme and fills in the appropriate fields
- * of the @uri structure
- *
- * path-noscheme = segment-nz-nc *( "/" segment )
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_path_no_scheme(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
-
- ret = rfc3986_parse_segment(&cur, ':', 0);
- if (ret != 0) {
- return ret;
- }
- while (*cur == '/') {
- cur++;
- ret = rfc3986_parse_segment(&cur, 0, 1);
- if (ret != 0) {
- return ret;
- }
- }
- if (uri != NULL) {
- g_free(uri->path);
- if (cur != *str) {
- if (uri->cleanup & 2) {
- uri->path = g_strndup(*str, cur - *str);
- } else {
- uri->path = g_uri_unescape_segment(*str, cur, NULL);
- }
- } else {
- uri->path = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_hier_part:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an hierarchical part and fills in the appropriate fields
- * of the @uri structure
- *
- * hier-part = "//" authority path-abempty
- * / path-absolute
- * / path-rootless
- * / path-empty
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_hier_part(URI *uri, const char **str)
-{
- const char *cur;
- int ret;
-
- cur = *str;
-
- if ((*cur == '/') && (*(cur + 1) == '/')) {
- cur += 2;
- ret = rfc3986_parse_authority(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- ret = rfc3986_parse_path_ab_empty(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- *str = cur;
- return 0;
- } else if (*cur == '/') {
- ret = rfc3986_parse_path_absolute(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- } else if (ISA_PCHAR(cur)) {
- ret = rfc3986_parse_path_rootless(uri, &cur);
- if (ret != 0) {
- return ret;
- }
- } else {
- /* path-empty is effectively empty */
- if (uri != NULL) {
- g_free(uri->path);
- uri->path = NULL;
- }
- }
- *str = cur;
- return 0;
-}
-
-/**
- * rfc3986_parse_relative_ref:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an URI string and fills in the appropriate fields
- * of the @uri structure
- *
- * relative-ref = relative-part [ "?" query ] [ "#" fragment ]
- * relative-part = "//" authority path-abempty
- * / path-absolute
- * / path-noscheme
- * / path-empty
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_relative_ref(URI *uri, const char *str)
-{
- int ret;
-
- if ((*str == '/') && (*(str + 1) == '/')) {
- str += 2;
- ret = rfc3986_parse_authority(uri, &str);
- if (ret != 0) {
- return ret;
- }
- ret = rfc3986_parse_path_ab_empty(uri, &str);
- if (ret != 0) {
- return ret;
- }
- } else if (*str == '/') {
- ret = rfc3986_parse_path_absolute(uri, &str);
- if (ret != 0) {
- return ret;
- }
- } else if (ISA_PCHAR(str)) {
- ret = rfc3986_parse_path_no_scheme(uri, &str);
- if (ret != 0) {
- return ret;
- }
- } else {
- /* path-empty is effectively empty */
- if (uri != NULL) {
- g_free(uri->path);
- uri->path = NULL;
- }
- }
-
- if (*str == '?') {
- str++;
- ret = rfc3986_parse_query(uri, &str);
- if (ret != 0) {
- return ret;
- }
- }
- if (*str == '#') {
- str++;
- ret = rfc3986_parse_fragment(uri, &str);
- if (ret != 0) {
- return ret;
- }
- }
- if (*str != 0) {
- uri_clean(uri);
- return 1;
- }
- return 0;
-}
-
-/**
- * rfc3986_parse:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an URI string and fills in the appropriate fields
- * of the @uri structure
- *
- * scheme ":" hier-part [ "?" query ] [ "#" fragment ]
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse(URI *uri, const char *str)
-{
- int ret;
-
- ret = rfc3986_parse_scheme(uri, &str);
- if (ret != 0) {
- return ret;
- }
- if (*str != ':') {
- return 1;
- }
- str++;
- ret = rfc3986_parse_hier_part(uri, &str);
- if (ret != 0) {
- return ret;
- }
- if (*str == '?') {
- str++;
- ret = rfc3986_parse_query(uri, &str);
- if (ret != 0) {
- return ret;
- }
- }
- if (*str == '#') {
- str++;
- ret = rfc3986_parse_fragment(uri, &str);
- if (ret != 0) {
- return ret;
- }
- }
- if (*str != 0) {
- uri_clean(uri);
- return 1;
- }
- return 0;
-}
-
-/**
- * rfc3986_parse_uri_reference:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an URI reference string and fills in the appropriate fields
- * of the @uri structure
- *
- * URI-reference = URI / relative-ref
- *
- * Returns 0 or the error code
- */
-static int rfc3986_parse_uri_reference(URI *uri, const char *str)
-{
- int ret;
-
- if (str == NULL) {
- return -1;
- }
- uri_clean(uri);
-
- /*
- * Try first to parse absolute refs, then fallback to relative if
- * it fails.
- */
- ret = rfc3986_parse(uri, str);
- if (ret != 0) {
- uri_clean(uri);
- ret = rfc3986_parse_relative_ref(uri, str);
- if (ret != 0) {
- uri_clean(uri);
- return ret;
- }
- }
- return 0;
-}
-
-/**
- * uri_parse:
- * @str: the URI string to analyze
- *
- * Parse an URI based on RFC 3986
- *
- * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
- *
- * Returns a newly built URI or NULL in case of error
- */
-URI *uri_parse(const char *str)
-{
- URI *uri;
- int ret;
-
- if (str == NULL) {
- return NULL;
- }
- uri = uri_new();
- ret = rfc3986_parse_uri_reference(uri, str);
- if (ret) {
- uri_free(uri);
- return NULL;
- }
- return uri;
-}
-
-/**
- * uri_parse_into:
- * @uri: pointer to an URI structure
- * @str: the string to analyze
- *
- * Parse an URI reference string based on RFC 3986 and fills in the
- * appropriate fields of the @uri structure
- *
- * URI-reference = URI / relative-ref
- *
- * Returns 0 or the error code
- */
-int uri_parse_into(URI *uri, const char *str)
-{
- return rfc3986_parse_uri_reference(uri, str);
-}
-
-/**
- * uri_parse_raw:
- * @str: the URI string to analyze
- * @raw: if 1 unescaping of URI pieces are disabled
- *
- * Parse an URI but allows to keep intact the original fragments.
- *
- * URI-reference = URI / relative-ref
- *
- * Returns a newly built URI or NULL in case of error
- */
-URI *uri_parse_raw(const char *str, int raw)
-{
- URI *uri;
- int ret;
-
- if (str == NULL) {
- return NULL;
- }
- uri = uri_new();
- if (raw) {
- uri->cleanup |= 2;
- }
- ret = uri_parse_into(uri, str);
- if (ret) {
- uri_free(uri);
- return NULL;
- }
- return uri;
-}
-
-/************************************************************************
- * *
- * Generic URI structure functions *
- * *
- ************************************************************************/
-
-/**
- * uri_new:
- *
- * Simply creates an empty URI
- *
- * Returns the new structure or NULL in case of error
- */
-URI *uri_new(void)
-{
- return g_new0(URI, 1);
-}
-
-/**
- * realloc2n:
- *
- * Function to handle properly a reallocation when saving an URI
- * Also imposes some limit on the length of an URI string output
- */
-static char *realloc2n(char *ret, int *max)
-{
- char *temp;
- int tmp;
-
- tmp = *max * 2;
- temp = g_realloc(ret, (tmp + 1));
- *max = tmp;
- return temp;
-}
-
-/**
- * uri_to_string:
- * @uri: pointer to an URI
- *
- * Save the URI as an escaped string
- *
- * Returns a new string (to be deallocated by caller)
- */
-char *uri_to_string(URI *uri)
-{
- char *ret = NULL;
- char *temp;
- const char *p;
- int len;
- int max;
-
- if (uri == NULL) {
- return NULL;
- }
-
- max = 80;
- ret = g_malloc(max + 1);
- len = 0;
-
- if (uri->scheme != NULL) {
- p = uri->scheme;
- while (*p != 0) {
- if (len >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = *p++;
- }
- if (len >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = ':';
- }
- if (uri->opaque != NULL) {
- p = uri->opaque;
- while (*p != 0) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) {
- ret[len++] = *p++;
- } else {
- int val = *(unsigned char *)p++;
- int hi = val / 0x10, lo = val % 0x10;
- ret[len++] = '%';
- ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0');
- ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0');
- }
- }
- } else {
- if (uri->server != NULL) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '/';
- ret[len++] = '/';
- if (uri->user != NULL) {
- p = uri->user;
- while (*p != 0) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) ||
- ((*(p) == ':')) || ((*(p) == '&')) || ((*(p) == '=')) ||
- ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) {
- ret[len++] = *p++;
- } else {
- int val = *(unsigned char *)p++;
- int hi = val / 0x10, lo = val % 0x10;
- ret[len++] = '%';
- ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0');
- ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0');
- }
- }
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '@';
- }
- p = uri->server;
- while (*p != 0) {
- if (len >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = *p++;
- }
- if (uri->port > 0) {
- if (len + 10 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- len += snprintf(&ret[len], max - len, ":%d", uri->port);
- }
- } else if (uri->authority != NULL) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '/';
- ret[len++] = '/';
- p = uri->authority;
- while (*p != 0) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) ||
- ((*(p) == ',')) || ((*(p) == ';')) || ((*(p) == ':')) ||
- ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) ||
- ((*(p) == '+'))) {
- ret[len++] = *p++;
- } else {
- int val = *(unsigned char *)p++;
- int hi = val / 0x10, lo = val % 0x10;
- ret[len++] = '%';
- ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0');
- ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0');
- }
- }
- } else if (uri->scheme != NULL) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '/';
- ret[len++] = '/';
- }
- if (uri->path != NULL) {
- p = uri->path;
- /*
- * the colon in file:///d: should not be escaped or
- * Windows accesses fail later.
- */
- if ((uri->scheme != NULL) && (p[0] == '/') &&
- (((p[1] >= 'a') && (p[1] <= 'z')) ||
- ((p[1] >= 'A') && (p[1] <= 'Z'))) &&
- (p[2] == ':') && (!strcmp(uri->scheme, "file"))) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = *p++;
- ret[len++] = *p++;
- ret[len++] = *p++;
- }
- while (*p != 0) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) ||
- ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||
- ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||
- ((*(p) == ','))) {
- ret[len++] = *p++;
- } else {
- int val = *(unsigned char *)p++;
- int hi = val / 0x10, lo = val % 0x10;
- ret[len++] = '%';
- ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0');
- ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0');
- }
- }
- }
- if (uri->query != NULL) {
- if (len + 1 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '?';
- p = uri->query;
- while (*p != 0) {
- if (len + 1 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = *p++;
- }
- }
- }
- if (uri->fragment != NULL) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len++] = '#';
- p = uri->fragment;
- while (*p != 0) {
- if (len + 3 >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) {
- ret[len++] = *p++;
- } else {
- int val = *(unsigned char *)p++;
- int hi = val / 0x10, lo = val % 0x10;
- ret[len++] = '%';
- ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0');
- ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0');
- }
- }
- }
- if (len >= max) {
- temp = realloc2n(ret, &max);
- ret = temp;
- }
- ret[len] = 0;
- return ret;
-}
-
-/**
- * uri_clean:
- * @uri: pointer to an URI
- *
- * Make sure the URI struct is free of content
- */
-static void uri_clean(URI *uri)
-{
- if (uri == NULL) {
- return;
- }
-
- g_free(uri->scheme);
- uri->scheme = NULL;
- g_free(uri->server);
- uri->server = NULL;
- g_free(uri->user);
- uri->user = NULL;
- g_free(uri->path);
- uri->path = NULL;
- g_free(uri->fragment);
- uri->fragment = NULL;
- g_free(uri->opaque);
- uri->opaque = NULL;
- g_free(uri->authority);
- uri->authority = NULL;
- g_free(uri->query);
- uri->query = NULL;
-}
-
-/**
- * uri_free:
- * @uri: pointer to an URI, NULL is ignored
- *
- * Free up the URI struct
- */
-void uri_free(URI *uri)
-{
- uri_clean(uri);
- g_free(uri);
-}
-
-/************************************************************************
- * *
- * Public functions *
- * *
- ************************************************************************/
-
-/*
- * Utility functions to help parse and assemble query strings.
- */
-
-struct QueryParams *query_params_new(int init_alloc)
-{
- struct QueryParams *ps;
-
- if (init_alloc <= 0) {
- init_alloc = 1;
- }
-
- ps = g_new(QueryParams, 1);
- ps->n = 0;
- ps->alloc = init_alloc;
- ps->p = g_new(QueryParam, ps->alloc);
-
- return ps;
-}
-
-/* Ensure there is space to store at least one more parameter
- * at the end of the set.
- */
-static int query_params_append(struct QueryParams *ps, const char *name,
- const char *value)
-{
- if (ps->n >= ps->alloc) {
- ps->p = g_renew(QueryParam, ps->p, ps->alloc * 2);
- ps->alloc *= 2;
- }
-
- ps->p[ps->n].name = g_strdup(name);
- ps->p[ps->n].value = g_strdup(value);
- ps->p[ps->n].ignore = 0;
- ps->n++;
-
- return 0;
-}
-
-void query_params_free(struct QueryParams *ps)
-{
- int i;
-
- for (i = 0; i < ps->n; ++i) {
- g_free(ps->p[i].name);
- g_free(ps->p[i].value);
- }
- g_free(ps->p);
- g_free(ps);
-}
-
-struct QueryParams *query_params_parse(const char *query)
-{
- struct QueryParams *ps;
- const char *end, *eq;
-
- ps = query_params_new(0);
- if (!query || query[0] == '\0') {
- return ps;
- }
-
- while (*query) {
- char *name = NULL, *value = NULL;
-
- /* Find the next separator, or end of the string. */
- end = strchr(query, '&');
- if (!end) {
- end = qemu_strchrnul(query, ';');
- }
-
- /* Find the first '=' character between here and end. */
- eq = strchr(query, '=');
- if (eq && eq >= end) {
- eq = NULL;
- }
-
- /* Empty section (eg. "&&"). */
- if (end == query) {
- goto next;
- }
-
- /* If there is no '=' character, then we have just "name"
- * and consistent with CGI.pm we assume value is "".
- */
- else if (!eq) {
- name = g_uri_unescape_segment(query, end, NULL);
- value = NULL;
- }
- /* Or if we have "name=" here (works around annoying
- * problem when calling uri_string_unescape with len = 0).
- */
- else if (eq + 1 == end) {
- name = g_uri_unescape_segment(query, eq, NULL);
- value = g_new0(char, 1);
- }
- /* If the '=' character is at the beginning then we have
- * "=value" and consistent with CGI.pm we _ignore_ this.
- */
- else if (query == eq) {
- goto next;
- }
-
- /* Otherwise it's "name=value". */
- else {
- name = g_uri_unescape_segment(query, eq, NULL);
- value = g_uri_unescape_segment(eq + 1, end, NULL);
- }
-
- /* Append to the parameter set. */
- query_params_append(ps, name, value);
- g_free(name);
- g_free(value);
-
- next:
- query = end;
- if (*query) {
- query++; /* skip '&' separator */
- }
- }
-
- return ps;
-}