summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--MAINTAINERS22
-rw-r--r--Makefile23
-rw-r--r--accel/Makefile.objs2
-rw-r--r--accel/kvm/Makefile.objs3
-rw-r--r--accel/kvm/kvm-all.c39
-rw-r--r--accel/kvm/sev-stub.c26
-rw-r--r--accel/stubs/kvm-stub.c10
-rw-r--r--accel/tcg/cpu-exec.c5
-rw-r--r--accel/tcg/translate-all.c18
-rw-r--r--block.c23
-rw-r--r--block/accounting.c91
-rw-r--r--block/backup.c5
-rw-r--r--block/block-backend.c63
-rw-r--r--block/commit.c2
-rw-r--r--block/crypto.c150
-rw-r--r--block/dirty-bitmap.c123
-rw-r--r--block/gluster.c2
-rw-r--r--block/iscsi.c2
-rw-r--r--block/mirror.c12
-rw-r--r--block/nbd-client.c154
-rw-r--r--block/nbd-client.h6
-rw-r--r--block/nbd.c3
-rw-r--r--block/parallels.c199
-rw-r--r--block/qapi.c53
-rw-r--r--block/qcow.c196
-rw-r--r--block/qcow2.c2
-rw-r--r--block/qed.c204
-rw-r--r--block/rbd.c8
-rw-r--r--block/sheepdog.c4
-rw-r--r--block/stream.c2
-rw-r--r--block/trace-events9
-rw-r--r--block/vdi.c147
-rw-r--r--block/vhdx.c216
-rw-r--r--block/vpc.c241
-rw-r--r--block/vvfat.c2
-rw-r--r--blockdev.c154
-rw-r--r--blockjob.c358
-rw-r--r--bsd-user/main.c6
-rw-r--r--chardev/char-mux.c33
-rw-r--r--chardev/char-socket.c181
-rw-r--r--chardev/char.c46
-rwxr-xr-xconfigure45
-rw-r--r--cpus.c24
-rw-r--r--default-configs/alpha-softmmu.mak5
-rw-r--r--default-configs/i386-softmmu.mak3
-rw-r--r--default-configs/x86_64-softmmu.mak3
-rw-r--r--default-configs/xtensa-linux-user.mak1
-rw-r--r--default-configs/xtensaeb-linux-user.mak1
-rw-r--r--docs/amd-memory-encryption.txt109
-rw-r--r--docs/devel/atomics.txt57
-rw-r--r--docs/devel/qapi-code-gen.txt87
-rw-r--r--docs/interop/qmp-spec.txt36
-rw-r--r--docs/replay.txt163
-rw-r--r--dump.c14
-rw-r--r--exec.c23
-rw-r--r--hmp-commands-info.hx16
-rw-r--r--hmp-commands.hx3
-rw-r--r--hmp.h1
-rw-r--r--hw/alpha/dp264.c13
-rw-r--r--hw/arm/bcm2836.c87
-rw-r--r--hw/arm/boot.c12
-rw-r--r--hw/arm/highbank.c1
-rw-r--r--hw/arm/msf2-soc.c1
-rw-r--r--hw/arm/raspi.c77
-rw-r--r--hw/arm/realview.c1
-rw-r--r--hw/arm/tosa.c1
-rw-r--r--hw/char/Makefile.objs1
-rw-r--r--hw/char/imx_serial.c44
-rw-r--r--hw/char/parallel-isa.c36
-rw-r--r--hw/char/parallel.c2
-rw-r--r--hw/core/machine.c22
-rw-r--r--hw/core/null-machine.c6
-rw-r--r--hw/display/vga-pci.c9
-rw-r--r--hw/dma/i82374.c3
-rw-r--r--hw/dma/i8257.c4
-rw-r--r--hw/i386/Makefile.objs4
-rw-r--r--hw/i386/acpi-build.c16
-rw-r--r--hw/i386/pc.c86
-rw-r--r--hw/i386/pc_piix.c7
-rw-r--r--hw/i386/pc_q35.c8
-rw-r--r--hw/i386/pc_sysfw.c13
-rw-r--r--hw/i386/vmmouse.c1
-rw-r--r--hw/i386/vmport.c1
-rw-r--r--hw/ide/ahci-allwinner.c1
-rw-r--r--hw/ide/cmd646.c1
-rw-r--r--hw/ide/ich.c1
-rw-r--r--hw/ide/isa.c1
-rw-r--r--hw/ide/microdrive.c1
-rw-r--r--hw/ide/mmio.c1
-rw-r--r--hw/ide/sii3112.c12
-rw-r--r--hw/input/pckbd.c2
-rw-r--r--hw/isa/Makefile.objs1
-rw-r--r--hw/isa/isa-bus.c26
-rw-r--r--hw/isa/isa-superio.c214
-rw-r--r--hw/isa/pc87312.c178
-rw-r--r--hw/isa/smc37c669-superio.c115
-rw-r--r--hw/isa/trace-events10
-rw-r--r--hw/isa/vt82c686.c22
-rw-r--r--hw/mips/mips_fulong2e.c86
-rw-r--r--hw/mips/mips_jazz.c13
-rw-r--r--hw/mips/mips_malta.c48
-rw-r--r--hw/mips/mips_r4k.c3
-rw-r--r--hw/misc/macio/macio.c2
-rw-r--r--hw/net/imx_fec.c28
-rw-r--r--hw/nios2/10m50_devboard.c2
-rw-r--r--hw/pci/pci.c61
-rw-r--r--hw/ppc/e500.c11
-rw-r--r--hw/ppc/mac_newworld.c1
-rw-r--r--hw/ppc/mac_oldworld.c1
-rw-r--r--hw/ppc/ppc440_pcix.c11
-rw-r--r--hw/ppc/prep.c9
-rw-r--r--hw/ppc/spapr.c12
-rw-r--r--hw/s390x/s390-stattrib.c7
-rw-r--r--hw/scsi/esp.c12
-rw-r--r--hw/scsi/lsi53c895a.c7
-rw-r--r--hw/scsi/mptendian.c1
-rw-r--r--hw/scsi/scsi-bus.c2
-rw-r--r--hw/scsi/scsi-disk.c17
-rw-r--r--hw/sd/core.c1
-rw-r--r--hw/sparc/sun4m.c5
-rw-r--r--hw/sparc64/sun4u.c6
-rw-r--r--hw/tricore/tricore_testboard.c2
-rw-r--r--hw/unicore32/puv3.c1
-rw-r--r--hw/vfio/Makefile.objs2
-rw-r--r--hw/vfio/common.c77
-rw-r--r--hw/vfio/display.c347
-rw-r--r--hw/vfio/pci.c32
-rw-r--r--hw/vfio/pci.h5
-rw-r--r--include/block/accounting.h35
-rw-r--r--include/block/blockjob.h71
-rw-r--r--include/block/blockjob_int.h17
-rw-r--r--include/block/dirty-bitmap.h7
-rw-r--r--include/block/nbd.h3
-rw-r--r--include/chardev/char-mux.h2
-rw-r--r--include/chardev/char.h2
-rw-r--r--include/exec/cpu-all.h6
-rw-r--r--include/exec/cpu_ldst.h16
-rw-r--r--include/hw/arm/bcm2836.h31
-rw-r--r--include/hw/arm/fsl-imx6.h4
-rw-r--r--include/hw/boards.h2
-rw-r--r--include/hw/char/imx_serial.h3
-rw-r--r--include/hw/char/parallel.h14
-rw-r--r--include/hw/dma/i8257.h (renamed from include/hw/isa/i8257.h)6
-rw-r--r--include/hw/i386/pc.h22
-rw-r--r--include/hw/input/i8042.h24
-rw-r--r--include/hw/isa/isa.h2
-rw-r--r--include/hw/isa/pc87312.h24
-rw-r--r--include/hw/isa/superio.h60
-rw-r--r--include/hw/isa/vt82c686.h4
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/scsi/esp.h10
-rw-r--r--include/hw/vfio/vfio-common.h23
-rw-r--r--include/migration/misc.h3
-rw-r--r--include/migration/register.h26
-rw-r--r--include/monitor/monitor.h2
-rw-r--r--include/qapi/qmp/dispatch.h7
-rw-r--r--include/qapi/qmp/qbool.h1
-rw-r--r--include/qapi/qmp/qdict.h1
-rw-r--r--include/qapi/qmp/qlist.h1
-rw-r--r--include/qapi/qmp/qlit.h4
-rw-r--r--include/qapi/qmp/qnum.h1
-rw-r--r--include/qapi/qmp/qobject.h29
-rw-r--r--include/qapi/qmp/qstring.h3
-rw-r--r--include/qemu/compiler.h12
-rw-r--r--include/qemu/cutils.h4
-rw-r--r--include/qemu/rcu.h16
-rw-r--r--include/qemu/sockets.h1
-rw-r--r--include/qemu/sys_membarrier.h27
-rw-r--r--include/qemu/timer.h14
-rw-r--r--include/qom/cpu.h16
-rw-r--r--include/qom/object.h11
-rw-r--r--include/standard-headers/drm/drm_fourcc.h411
-rw-r--r--include/standard-headers/linux/input-event-codes.h1
-rw-r--r--include/standard-headers/linux/input.h11
-rw-r--r--include/standard-headers/linux/pci_regs.h30
-rw-r--r--include/standard-headers/linux/virtio_ring.h2
-rw-r--r--include/standard-headers/rdma/vmw_pvrdma-abi.h13
-rw-r--r--include/sysemu/kvm.h17
-rw-r--r--include/sysemu/replay.h16
-rw-r--r--include/sysemu/sev.h21
-rw-r--r--include/sysemu/sysemu.h2
-rw-r--r--include/ui/console.h2
-rw-r--r--include/ui/qemu-pixman.h5
-rw-r--r--io/channel-util.c13
-rw-r--r--linux-headers/asm-powerpc/kvm.h2
-rw-r--r--linux-headers/asm-powerpc/unistd.h3
-rw-r--r--linux-headers/asm-s390/unistd.h401
-rw-r--r--linux-headers/asm-s390/unistd_32.h364
-rw-r--r--linux-headers/asm-s390/unistd_64.h331
-rw-r--r--linux-headers/asm-x86/kvm_para.h5
-rw-r--r--linux-headers/linux/kvm.h92
-rw-r--r--linux-headers/linux/psci.h3
-rw-r--r--linux-headers/linux/psp-sev.h142
-rw-r--r--linux-headers/linux/vfio.h72
-rw-r--r--linux-user/elfload.c226
-rw-r--r--linux-user/main.c354
-rw-r--r--linux-user/mmap.c43
-rw-r--r--linux-user/qemu.h6
-rw-r--r--linux-user/signal.c260
-rw-r--r--linux-user/syscall.c20
-rw-r--r--linux-user/syscall_defs.h112
-rw-r--r--linux-user/unicore32/syscall_nr.h371
-rw-r--r--linux-user/unicore32/target_cpu.h27
-rw-r--r--linux-user/unicore32/target_signal.h30
-rw-r--r--linux-user/unicore32/target_structs.h58
-rw-r--r--linux-user/unicore32/target_syscall.h62
-rw-r--r--linux-user/unicore32/termbits.h2
-rw-r--r--linux-user/xtensa/syscall.h0
-rw-r--r--linux-user/xtensa/syscall_nr.h437
-rw-r--r--linux-user/xtensa/target_cpu.h22
-rw-r--r--linux-user/xtensa/target_elf.h (renamed from linux-user/unicore32/target_elf.h)8
-rw-r--r--linux-user/xtensa/target_signal.h28
-rw-r--r--linux-user/xtensa/target_structs.h28
-rw-r--r--linux-user/xtensa/target_syscall.h49
-rw-r--r--linux-user/xtensa/termbits.h328
-rw-r--r--migration/Makefile.objs1
-rw-r--r--migration/block-dirty-bitmap.c746
-rw-r--r--migration/block.c7
-rw-r--r--migration/migration.c30
-rw-r--r--migration/migration.h4
-rw-r--r--migration/qemu-file.c13
-rw-r--r--migration/qemu-file.h2
-rw-r--r--migration/ram.c9
-rw-r--r--migration/savevm.c33
-rw-r--r--migration/savevm.h5
-rw-r--r--migration/trace-events16
-rw-r--r--monitor.c727
-rw-r--r--nbd/client.c117
-rw-r--r--nbd/server.c701
-rw-r--r--pc-bios/bios-256k.binbin262144 -> 262144 bytes
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin38400 -> 38400 bytes
-rw-r--r--pc-bios/vgabios-qxl.binbin38912 -> 38912 bytes
-rw-r--r--pc-bios/vgabios-stdvga.binbin38912 -> 38912 bytes
-rw-r--r--pc-bios/vgabios-virtio.binbin38912 -> 38912 bytes
-rw-r--r--pc-bios/vgabios-vmware.binbin38912 -> 38912 bytes
-rw-r--r--pc-bios/vgabios.binbin38400 -> 38400 bytes
-rw-r--r--qapi/block-core.json483
-rw-r--r--qapi/introspect.json6
-rw-r--r--qapi/migration.json6
-rw-r--r--qapi/misc.json235
-rw-r--r--qapi/qmp-dispatch.c35
-rw-r--r--qapi/qobject-input-visitor.c24
-rw-r--r--qapi/qobject-output-visitor.c4
-rw-r--r--qapi/sockets.json7
-rw-r--r--qdev-monitor.c9
-rw-r--r--qemu-doc.texi51
-rw-r--r--qemu-img.c2
-rw-r--r--qemu-io.c2
-rw-r--r--qemu-nbd.c2
-rw-r--r--qemu-options.hx53
-rw-r--r--qga/main.c4
-rw-r--r--qmp.c18
-rw-r--r--qobject/json-parser.c13
-rw-r--r--qobject/qbool.c15
-rw-r--r--qobject/qdict.c65
-rw-r--r--qobject/qjson.c10
-rw-r--r--qobject/qlist.c17
-rw-r--r--qobject/qlit.c47
-rw-r--r--qobject/qnum.c17
-rw-r--r--qobject/qstring.c38
-rw-r--r--qom/cpu.c61
-rw-r--r--qom/object.c28
-rw-r--r--replay/replay-audio.c14
-rwxr-xr-xreplay/replay-char.c21
-rw-r--r--replay/replay-events.c75
-rw-r--r--replay/replay-internal.c43
-rw-r--r--replay/replay-internal.h17
-rw-r--r--replay/replay-snapshot.c12
-rw-r--r--replay/replay-time.c10
-rw-r--r--replay/replay.c58
m---------roms/seabios0
-rwxr-xr-xscripts/checkpatch.pl3
-rw-r--r--scripts/qapi/commands.py18
-rw-r--r--scripts/qapi/common.py15
-rw-r--r--scripts/qapi/doc.py9
-rw-r--r--scripts/qapi/introspect.py83
-rwxr-xr-xscripts/qemu-binfmt-conf.sh37
-rwxr-xr-xscripts/replay-dump.py308
-rwxr-xr-xscripts/update-linux-headers.sh10
-rw-r--r--scsi/qemu-pr-helper.c2
-rw-r--r--stubs/machine-init-done.c2
-rw-r--r--stubs/replay.c8
-rw-r--r--target/alpha/cpu.c15
-rw-r--r--target/alpha/cpu.h3
-rw-r--r--target/arm/cpu.h3
-rw-r--r--target/cris/cpu.h3
-rw-r--r--target/hppa/cpu.c15
-rw-r--r--target/hppa/cpu.h2
-rw-r--r--target/i386/Makefile.objs2
-rw-r--r--target/i386/cpu.c29
-rw-r--r--target/i386/cpu.h6
-rw-r--r--target/i386/kvm.c13
-rw-r--r--target/i386/machine.c20
-rw-r--r--target/i386/monitor.c66
-rw-r--r--target/i386/sev-stub.c51
-rw-r--r--target/i386/sev.c811
-rw-r--r--target/i386/sev_i386.h88
-rw-r--r--target/i386/trace-events10
-rw-r--r--target/lm32/cpu.c15
-rw-r--r--target/lm32/cpu.h3
-rw-r--r--target/m68k/cpu.h3
-rw-r--r--target/m68k/fpu_helper.c61
-rw-r--r--target/m68k/helper.h11
-rw-r--r--target/m68k/softfloat.c1637
-rw-r--r--target/m68k/softfloat.h11
-rw-r--r--target/m68k/softfloat_fpsp_tables.h267
-rw-r--r--target/m68k/translate.c140
-rw-r--r--target/microblaze/cpu.h2
-rw-r--r--target/mips/cpu.h3
-rw-r--r--target/moxie/cpu.h3
-rw-r--r--target/nios2/cpu.h2
-rw-r--r--target/openrisc/cpu.h3
-rw-r--r--target/ppc/cpu.h3
-rw-r--r--target/ppc/translate.c9
-rw-r--r--target/riscv/cpu.c12
-rw-r--r--target/riscv/cpu.h1
-rw-r--r--target/s390x/cpu.h3
-rw-r--r--target/s390x/cpu_models.c2
-rw-r--r--target/sh4/cpu.c15
-rw-r--r--target/sh4/cpu.h3
-rw-r--r--target/sparc/cpu.h5
-rw-r--r--target/tilegx/cpu.h2
-rw-r--r--target/tricore/cpu.h3
-rw-r--r--target/tricore/helper.c2
-rw-r--r--target/unicore32/cpu.h3
-rw-r--r--target/xtensa/Makefile.objs3
-rw-r--r--target/xtensa/cpu.c26
-rw-r--r--target/xtensa/cpu.h63
-rw-r--r--target/xtensa/gdbstub.c14
-rw-r--r--target/xtensa/helper.c59
-rw-r--r--target/xtensa/helper.h4
-rw-r--r--target/xtensa/op_helper.c50
-rw-r--r--target/xtensa/overlay_tool.h11
-rw-r--r--target/xtensa/translate.c99
-rw-r--r--tcg/i386/tcg-target.inc.c9
-rw-r--r--tcg/tcg-op-gvec.c440
-rw-r--r--tcg/tcg-op.c24
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.include15
-rw-r--r--tests/boot-serial-test.c6
-rw-r--r--tests/check-qdict.c20
-rw-r--r--tests/check-qjson.c41
-rw-r--r--tests/check-qlist.c4
-rw-r--r--tests/check-qlit.c30
-rw-r--r--tests/check-qnum.c4
-rw-r--r--tests/check-qobject.c2
-rw-r--r--tests/check-qstring.c2
-rw-r--r--tests/device-introspect-test.c14
-rw-r--r--tests/libqtest.c6
-rw-r--r--tests/machine-none-test.c103
-rw-r--r--tests/numa-test.c8
-rw-r--r--tests/qapi-schema/test-qapi.py2
-rwxr-xr-xtests/qemu-iotests/0306
-rwxr-xr-xtests/qemu-iotests/0331
-rwxr-xr-xtests/qemu-iotests/05517
-rwxr-xr-xtests/qemu-iotests/056187
-rw-r--r--tests/qemu-iotests/056.out4
-rwxr-xr-xtests/qemu-iotests/08920
-rw-r--r--tests/qemu-iotests/089.out8
-rw-r--r--tests/qemu-iotests/109.out24
-rw-r--r--tests/qemu-iotests/146.out2
-rwxr-xr-xtests/qemu-iotests/15312
-rw-r--r--tests/qemu-iotests/153.out5
-rwxr-xr-xtests/qemu-iotests/169156
-rw-r--r--tests/qemu-iotests/169.out5
-rwxr-xr-xtests/qemu-iotests/1812
-rwxr-xr-xtests/qemu-iotests/199118
-rw-r--r--tests/qemu-iotests/199.out5
-rwxr-xr-xtests/qemu-iotests/20855
-rw-r--r--tests/qemu-iotests/208.out9
-rwxr-xr-xtests/qemu-iotests/20934
-rw-r--r--tests/qemu-iotests/209.out2
-rwxr-xr-xtests/qemu-iotests/210210
-rw-r--r--tests/qemu-iotests/210.out136
-rwxr-xr-xtests/qemu-iotests/check13
-rw-r--r--tests/qemu-iotests/common.rc2
-rw-r--r--tests/qemu-iotests/group5
-rw-r--r--tests/qemu-iotests/iotests.py56
-rw-r--r--tests/qmp-test.c102
-rw-r--r--tests/qom-test.c4
-rw-r--r--tests/rcutorture.c4
-rw-r--r--tests/socket-helpers.c149
-rw-r--r--tests/socket-helpers.h42
-rw-r--r--tests/test-bdrv-drain.c5
-rw-r--r--tests/test-blockjob-txn.c27
-rw-r--r--tests/test-blockjob.c233
-rw-r--r--tests/test-char.c50
-rw-r--r--tests/test-cutils.c657
-rw-r--r--tests/test-io-channel-socket.c72
-rw-r--r--tests/test-keyval.c8
-rw-r--r--tests/test-qga.c19
-rw-r--r--tests/test-qmp-cmds.c12
-rw-r--r--tests/test-qmp-event.c16
-rw-r--r--tests/test-qobject-input-visitor.c21
-rw-r--r--tests/test-qobject-output-visitor.c54
-rw-r--r--tests/test-util-sockets.c266
-rw-r--r--tests/test-x86-cpuid-compat.c17
-rw-r--r--tests/virtio-scsi-test.c6
-rw-r--r--trace-events3
-rw-r--r--ui/cocoa.m2
-rw-r--r--ui/console.c79
-rw-r--r--ui/qemu-pixman.c22
-rw-r--r--ui/trace-events2
-rw-r--r--util/Makefile.objs1
-rw-r--r--util/cutils.c109
-rw-r--r--util/keyval.c4
-rw-r--r--util/main-loop.c15
-rw-r--r--util/qemu-config.c2
-rw-r--r--util/qemu-option.c6
-rw-r--r--util/qemu-sockets.c36
-rw-r--r--util/qemu-timer.c12
-rw-r--r--util/rcu.c15
-rw-r--r--util/sys_membarrier.c50
-rw-r--r--vl.c25
416 files changed, 18492 insertions, 3873 deletions
diff --git a/.travis.yml b/.travis.yml
index 79377c8de0..c1e99237b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,9 +49,10 @@ env:
- TEST_CMD="make check"
- MAKEFLAGS="-j3"
matrix:
- - CONFIG=""
- - CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
- - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
+ - CONFIG="--disable-system"
+ - CONFIG="--disable-user"
+ - CONFIG="--enable-debug --enable-debug-tcg"
+ - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb --disable-user"
- CONFIG="--enable-modules --disable-linux-user"
- CONFIG="--with-coroutine=ucontext --disable-linux-user"
- CONFIG="--with-coroutine=sigaltstack --disable-linux-user"
diff --git a/MAINTAINERS b/MAINTAINERS
index 354a18ce49..6fa36c7a2d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -127,7 +127,6 @@ Alpha
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: target/alpha/
-F: hw/alpha/
F: tests/tcg/alpha/
F: disas/alpha.c
@@ -290,7 +289,7 @@ T: git git://github.com/ehabkost/qemu.git x86-next
Xtensa
M: Max Filippov <jcmvbkbc@gmail.com>
-W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
+W: http://wiki.osll.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
S: Maintained
F: target/xtensa/
F: hw/xtensa/
@@ -413,6 +412,12 @@ F: include/*/*win32*
X: qga/*win32*
F: qemu.nsi
+Alpha Machines
+M: Richard Henderson <rth@twiddle.net>
+S: Maintained
+F: hw/alpha/
+F: hw/isa/smc37c669-superio.c
+
ARM Machines
------------
Allwinner-a10
@@ -700,6 +705,8 @@ Fulong 2E
M: Yongbok Kim <yongbok.kim@mips.com>
S: Odd Fixes
F: hw/mips/mips_fulong2e.c
+F: hw/isa/vt82c686.c
+F: include/hw/isa/vt82c686.h
Boston
M: Paul Burton <paul.burton@mips.com>
@@ -776,9 +783,10 @@ F: hw/ppc/prep_systemio.c
F: hw/ppc/rs6000_mc.c
F: hw/pci-host/prep.[hc]
F: hw/isa/i82378.c
-F: hw/isa/pc87312.[hc]
+F: hw/isa/pc87312.c
F: hw/dma/i82374.c
F: hw/timer/m48t59-isa.c
+F: include/hw/isa/pc87312.h
F: include/hw/timer/m48t59.h
F: pc-bios/ppc_rom.bin
@@ -924,7 +932,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
M: Paolo Bonzini <pbonzini@redhat.com>
S: Supported
F: hw/char/debugcon.c
-F: hw/char/parallel.c
+F: hw/char/parallel*
F: hw/char/serial*
F: hw/dma/i8257*
F: hw/i2c/pm_smbus.c
@@ -932,6 +940,7 @@ F: hw/input/pckbd.c
F: hw/intc/apic*
F: hw/intc/ioapic*
F: hw/intc/i8259*
+F: hw/isa/isa-superio.c
F: hw/misc/debugexit.c
F: hw/misc/pc-testdev.c
F: hw/timer/hpet*
@@ -939,8 +948,11 @@ F: hw/timer/i8254*
F: hw/timer/mc146818rtc*
F: hw/watchdog/wdt_ib700.c
F: include/hw/display/vga.h
+F: include/hw/char/parallel.h
+F: include/hw/dma/i8257.h
F: include/hw/i2c/pm_smbus.h
-F: include/hw/isa/i8257.h
+F: include/hw/input/i8042.h
+F: include/hw/isa/superio.h
F: include/hw/timer/hpet.h
F: include/hw/timer/i8254*
F: include/hw/timer/mc146818rtc*
diff --git a/Makefile b/Makefile
index f799390464..727ef118f3 100644
--- a/Makefile
+++ b/Makefile
@@ -438,21 +438,23 @@ all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
qemu-version.h: FORCE
$(call quiet-command, \
(cd $(SRC_PATH); \
- printf '#define QEMU_PKGVERSION '; \
if test -n "$(PKGVERSION)"; then \
- printf '"$(PKGVERSION)"\n'; \
+ pkgvers="$(PKGVERSION)"; \
else \
if test -d .git; then \
- printf '" ('; \
- git describe --match 'v*' 2>/dev/null | tr -d '\n'; \
+ pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\
if ! git diff-index --quiet HEAD &>/dev/null; then \
- printf -- '-dirty'; \
+ pkgvers="$${pkgvers}-dirty"; \
fi; \
- printf ')"\n'; \
- else \
- printf '""\n'; \
fi; \
- fi) > $@.tmp)
+ fi; \
+ printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \
+ if test -n "$${pkgvers}"; then \
+ printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \
+ else \
+ printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \
+ fi; \
+ ) > $@.tmp)
$(call quiet-command, if ! cmp -s $@ $@.tmp; then \
mv $@.tmp $@; \
else \
@@ -1052,6 +1054,9 @@ printgen:
help:
@echo 'Generic targets:'
@echo ' all - Build all'
+ifdef CONFIG_MODULES
+ @echo ' modules - Build all modules'
+endif
@echo ' dir/file.o - Build specified target only'
@echo ' install - Install QEMU, documentation and tools'
@echo ' ctags/TAGS - Generate tags file for editors'
diff --git a/accel/Makefile.objs b/accel/Makefile.objs
index 10666eda71..c3718a10c5 100644
--- a/accel/Makefile.objs
+++ b/accel/Makefile.objs
@@ -1,4 +1,4 @@
obj-$(CONFIG_SOFTMMU) += accel.o
-obj-y += kvm/
+obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/
obj-y += stubs/
diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs
index 85351e7de7..fdfa481578 100644
--- a/accel/kvm/Makefile.objs
+++ b/accel/kvm/Makefile.objs
@@ -1 +1,2 @@
-obj-$(CONFIG_KVM) += kvm-all.o
+obj-y += kvm-all.o
+obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index b91fcb7160..ffee68e603 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -38,6 +38,7 @@
#include "qemu/event_notifier.h"
#include "trace.h"
#include "hw/irq.h"
+#include "sysemu/sev.h"
#include "hw/boards.h"
@@ -103,6 +104,10 @@ struct KVMState
#endif
KVMMemoryListener memory_listener;
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
+
+ /* memory encryption */
+ void *memcrypt_handle;
+ int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
};
KVMState *kvm_state;
@@ -138,6 +143,26 @@ int kvm_get_max_memslots(void)
return s->nr_slots;
}
+bool kvm_memcrypt_enabled(void)
+{
+ if (kvm_state && kvm_state->memcrypt_handle) {
+ return true;
+ }
+
+ return false;
+}
+
+int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
+{
+ if (kvm_state->memcrypt_handle &&
+ kvm_state->memcrypt_encrypt_data) {
+ return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
+ ptr, len);
+ }
+
+ return 1;
+}
+
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{
KVMState *s = kvm_state;
@@ -1636,6 +1661,20 @@ static int kvm_init(MachineState *ms)
kvm_state = s;
+ /*
+ * if memory encryption object is specified then initialize the memory
+ * encryption context.
+ */
+ if (ms->memory_encryption) {
+ kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
+ if (!kvm_state->memcrypt_handle) {
+ ret = -1;
+ goto err;
+ }
+
+ kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
+ }
+
ret = kvm_arch_init(ms, s);
if (ret < 0) {
goto err;
diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c
new file mode 100644
index 0000000000..4f97452585
--- /dev/null
+++ b/accel/kvm/sev-stub.c
@@ -0,0 +1,26 @@
+/*
+ * QEMU SEV stub
+ *
+ * Copyright Advanced Micro Devices 2018
+ *
+ * Authors:
+ * Brijesh Singh <brijesh.singh@amd.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 "qemu-common.h"
+#include "sysemu/sev.h"
+
+int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
+{
+ abort();
+}
+
+void *sev_guest_init(const char *id)
+{
+ return NULL;
+}
diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c
index c964af3e1c..02d5170031 100644
--- a/accel/stubs/kvm-stub.c
+++ b/accel/stubs/kvm-stub.c
@@ -105,6 +105,16 @@ int kvm_on_sigbus(int code, void *addr)
return 1;
}
+bool kvm_memcrypt_enabled(void)
+{
+ return false;
+}
+
+int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
+{
+ return 1;
+}
+
#ifndef CONFIG_USER_ONLY
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
{
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 280200f737..9cc697205c 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -585,6 +585,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt();
+ cpu->exception_index = -1;
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
@@ -606,7 +607,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
atomic_set(&cpu->exit_request, 0);
- cpu->exception_index = EXCP_INTERRUPT;
+ if (cpu->exception_index == -1) {
+ cpu->exception_index = EXCP_INTERRUPT;
+ }
return true;
}
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 67795cd78c..5ad1b919bc 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1728,7 +1728,8 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
CPUArchState *env = cpu->env_ptr;
#endif
TranslationBlock *tb;
- uint32_t n;
+ uint32_t n, flags;
+ target_ulong pc, cs_base;
tb_lock();
tb = tb_find_pc(retaddr);
@@ -1766,8 +1767,14 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
cpu_abort(cpu, "TB too big during recompile");
}
- /* Adjust the execution state of the next TB. */
- cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n;
+ pc = tb->pc;
+ cs_base = tb->cs_base;
+ flags = tb->flags;
+ tb_phys_invalidate(tb, -1);
+
+ /* Execute one IO instruction without caching
+ instead of creating large TB. */
+ cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | CF_NOCACHE | 1;
if (tb->cflags & CF_NOCACHE) {
if (tb->orig_tb) {
@@ -1778,6 +1785,11 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
tb_remove(tb);
}
+ /* Generate new TB instead of the current one. */
+ /* FIXME: In theory this could raise an exception. In practice
+ we have already translated the block once so it's probably ok. */
+ tb_gen_code(cpu, pc, cs_base, flags, curr_cflags() | CF_LAST_IO | n);
+
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
* the first in the TB) then we end up generating a whole new TB and
* repeating the fault, which is horribly inefficient.
diff --git a/block.c b/block.c
index 75a9fd49de..a2caadf0a0 100644
--- a/block.c
+++ b/block.c
@@ -33,6 +33,7 @@
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qnull.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qapi-visit-block-core.h"
@@ -1457,7 +1458,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
return NULL;
}
- options = qobject_to_qdict(options_obj);
+ options = qobject_to(QDict, options_obj);
if (!options) {
qobject_decref(options_obj);
error_setg(errp, "Invalid JSON object given");
@@ -2433,7 +2434,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
}
visit_complete(v, &obj);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
/* bdrv_open_inherit() defaults to the values in bdrv_flags (for
@@ -2645,7 +2646,13 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
/* See cautionary note on accessing @options above */
backing = qdict_get_try_str(options, "backing");
- if (backing && *backing == '\0') {
+ if (qobject_to(QNull, qdict_get(options, "backing")) != NULL ||
+ (backing && *backing == '\0'))
+ {
+ if (backing) {
+ warn_report("Use of \"backing\": \"\" is deprecated; "
+ "use \"backing\": null instead");
+ }
flags |= BDRV_O_NO_BACKING;
qdict_del(options, "backing");
}
@@ -2883,8 +2890,16 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
/* Inherit from parent node */
if (parent_options) {
+ QemuOpts *opts;
+ QDict *options_copy;
assert(!flags);
role->inherit_options(&flags, options, parent_flags, parent_options);
+ options_copy = qdict_clone_shallow(options);
+ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options_copy, NULL);
+ update_flags_from_options(&flags, opts);
+ qemu_opts_del(opts);
+ QDECREF(options_copy);
}
/* Old values are used for options that aren't set yet */
@@ -3671,12 +3686,12 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
GSList *ignore_children = g_slist_prepend(NULL, c);
bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
ignore_children, &local_err);
+ g_slist_free(ignore_children);
if (local_err) {
ret = -EPERM;
error_report_err(local_err);
goto exit;
}
- g_slist_free(ignore_children);
/* If so, update the backing file path in the image file */
if (c->role->update_filename) {
diff --git a/block/accounting.c b/block/accounting.c
index 87ef5bbfaa..70a3d9a426 100644
--- a/block/accounting.c
+++ b/block/accounting.c
@@ -94,6 +94,94 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
cookie->type = type;
}
+/* block_latency_histogram_compare_func:
+ * Compare @key with interval [@it[0], @it[1]).
+ * Return: -1 if @key < @it[0]
+ * 0 if @key in [@it[0], @it[1])
+ * +1 if @key >= @it[1]
+ */
+static int block_latency_histogram_compare_func(const void *key, const void *it)
+{
+ uint64_t k = *(uint64_t *)key;
+ uint64_t a = ((uint64_t *)it)[0];
+ uint64_t b = ((uint64_t *)it)[1];
+
+ return k < a ? -1 : (k < b ? 0 : 1);
+}
+
+static void block_latency_histogram_account(BlockLatencyHistogram *hist,
+ int64_t latency_ns)
+{
+ uint64_t *pos;
+
+ if (hist->bins == NULL) {
+ /* histogram disabled */
+ return;
+ }
+
+
+ if (latency_ns < hist->boundaries[0]) {
+ hist->bins[0]++;
+ return;
+ }
+
+ if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
+ hist->bins[hist->nbins - 1]++;
+ return;
+ }
+
+ pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
+ sizeof(hist->boundaries[0]),
+ block_latency_histogram_compare_func);
+ assert(pos != NULL);
+
+ hist->bins[pos - hist->boundaries + 1]++;
+}
+
+int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
+ uint64List *boundaries)
+{
+ BlockLatencyHistogram *hist = &stats->latency_histogram[type];
+ uint64List *entry;
+ uint64_t *ptr;
+ uint64_t prev = 0;
+ int new_nbins = 1;
+
+ for (entry = boundaries; entry; entry = entry->next) {
+ if (entry->value <= prev) {
+ return -EINVAL;
+ }
+ new_nbins++;
+ prev = entry->value;
+ }
+
+ hist->nbins = new_nbins;
+ g_free(hist->boundaries);
+ hist->boundaries = g_new(uint64_t, hist->nbins - 1);
+ for (entry = boundaries, ptr = hist->boundaries; entry;
+ entry = entry->next, ptr++)
+ {
+ *ptr = entry->value;
+ }
+
+ g_free(hist->bins);
+ hist->bins = g_new0(uint64_t, hist->nbins);
+
+ return 0;
+}
+
+void block_latency_histograms_clear(BlockAcctStats *stats)
+{
+ int i;
+
+ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
+ BlockLatencyHistogram *hist = &stats->latency_histogram[i];
+ g_free(hist->bins);
+ g_free(hist->boundaries);
+ memset(hist, 0, sizeof(*hist));
+ }
+}
+
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
bool failed)
{
@@ -116,6 +204,9 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
stats->nr_ops[cookie->type]++;
}
+ block_latency_histogram_account(&stats->latency_histogram[cookie->type],
+ latency_ns);
+
if (!failed || stats->account_failed) {
stats->total_time_ns[cookie->type] += latency_ns;
stats->last_access_time_ns = time_ns;
diff --git a/block/backup.c b/block/backup.c
index 4a16a37229..453cd62c24 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -206,7 +206,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
BdrvDirtyBitmap *bm;
BlockDriverState *bs = blk_bs(job->common.blk);
- if (ret < 0 || block_job_is_cancelled(&job->common)) {
+ if (ret < 0) {
/* Merge the successor back into the parent, delete nothing. */
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
assert(bm);
@@ -621,7 +621,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
}
/* job->common.len is fixed, so we can't allow resize */
- job = block_job_create(job_id, &backup_job_driver, bs,
+ job = block_job_create(job_id, &backup_job_driver, txn, bs,
BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
@@ -677,7 +677,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
job->common.len = len;
- block_job_txn_add_job(txn, &job->common);
return &job->common;
diff --git a/block/block-backend.c b/block/block-backend.c
index f2e0a855ff..681b240b12 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -31,6 +31,13 @@
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb);
+typedef struct BlockBackendAioNotifier {
+ void (*attached_aio_context)(AioContext *new_context, void *opaque);
+ void (*detach_aio_context)(void *opaque);
+ void *opaque;
+ QLIST_ENTRY(BlockBackendAioNotifier) list;
+} BlockBackendAioNotifier;
+
struct BlockBackend {
char *name;
int refcnt;
@@ -69,6 +76,7 @@ struct BlockBackend {
bool allow_write_beyond_eof;
NotifierList remove_bs_notifiers, insert_bs_notifiers;
+ QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
int quiesce_counter;
VMChangeStateEntry *vmsh;
@@ -247,6 +255,36 @@ static int blk_root_inactivate(BdrvChild *child)
return 0;
}
+static void blk_root_attach(BdrvChild *child)
+{
+ BlockBackend *blk = child->opaque;
+ BlockBackendAioNotifier *notifier;
+
+ trace_blk_root_attach(child, blk, child->bs);
+
+ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
+ bdrv_add_aio_context_notifier(child->bs,
+ notifier->attached_aio_context,
+ notifier->detach_aio_context,
+ notifier->opaque);
+ }
+}
+
+static void blk_root_detach(BdrvChild *child)
+{
+ BlockBackend *blk = child->opaque;
+ BlockBackendAioNotifier *notifier;
+
+ trace_blk_root_detach(child, blk, child->bs);
+
+ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
+ bdrv_remove_aio_context_notifier(child->bs,
+ notifier->attached_aio_context,
+ notifier->detach_aio_context,
+ notifier->opaque);
+ }
+}
+
static const BdrvChildRole child_root = {
.inherit_options = blk_root_inherit_options,
@@ -260,6 +298,9 @@ static const BdrvChildRole child_root = {
.activate = blk_root_activate,
.inactivate = blk_root_inactivate,
+
+ .attach = blk_root_attach,
+ .detach = blk_root_detach,
};
/*
@@ -287,6 +328,7 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm)
notifier_list_init(&blk->remove_bs_notifiers);
notifier_list_init(&blk->insert_bs_notifiers);
+ QLIST_INIT(&blk->aio_notifiers);
QTAILQ_INSERT_TAIL(&block_backends, blk, link);
return blk;
@@ -364,6 +406,7 @@ static void blk_delete(BlockBackend *blk)
}
assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers));
assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers));
+ assert(QLIST_EMPTY(&blk->aio_notifiers));
QTAILQ_REMOVE(&block_backends, blk, link);
drive_info_del(blk->legacy_dinfo);
block_acct_cleanup(&blk->stats);
@@ -1857,8 +1900,15 @@ void blk_add_aio_context_notifier(BlockBackend *blk,
void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque)
{
+ BlockBackendAioNotifier *notifier;
BlockDriverState *bs = blk_bs(blk);
+ notifier = g_new(BlockBackendAioNotifier, 1);
+ notifier->attached_aio_context = attached_aio_context;
+ notifier->detach_aio_context = detach_aio_context;
+ notifier->opaque = opaque;
+ QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list);
+
if (bs) {
bdrv_add_aio_context_notifier(bs, attached_aio_context,
detach_aio_context, opaque);
@@ -1871,12 +1921,25 @@ void blk_remove_aio_context_notifier(BlockBackend *blk,
void (*detach_aio_context)(void *),
void *opaque)
{
+ BlockBackendAioNotifier *notifier;
BlockDriverState *bs = blk_bs(blk);
if (bs) {
bdrv_remove_aio_context_notifier(bs, attached_aio_context,
detach_aio_context, opaque);
}
+
+ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) {
+ if (notifier->attached_aio_context == attached_aio_context &&
+ notifier->detach_aio_context == detach_aio_context &&
+ notifier->opaque == opaque) {
+ QLIST_REMOVE(notifier, list);
+ g_free(notifier);
+ return;
+ }
+ }
+
+ abort();
}
void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify)
diff --git a/block/commit.c b/block/commit.c
index 1943c9c3e1..ab4fa3c3cf 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -289,7 +289,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
return;
}
- s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
+ s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
if (!s) {
return;
diff --git a/block/crypto.c b/block/crypto.c
index e6095e7807..e0b8856f74 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -71,8 +71,6 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
struct BlockCryptoCreateData {
- const char *filename;
- QemuOpts *opts;
BlockBackend *blk;
uint64_t size;
};
@@ -103,27 +101,18 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
Error **errp)
{
struct BlockCryptoCreateData *data = opaque;
- int ret;
+
+ if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
+ error_setg(errp, "The requested file size is too large");
+ return -EFBIG;
+ }
/* User provided size should reflect amount of space made
* available to the guest, so we must take account of that
* which will be used by the crypto header
*/
- data->size += headerlen;
-
- qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
- ret = bdrv_create_file(data->filename, data->opts, errp);
- if (ret < 0) {
- return -1;
- }
-
- data->blk = blk_new_open(data->filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
- if (!data->blk) {
- return -1;
- }
-
- return 0;
+ return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
+ errp);
}
@@ -322,30 +311,29 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
}
-static int block_crypto_create_generic(QCryptoBlockFormat format,
- const char *filename,
- QemuOpts *opts,
- Error **errp)
+static int block_crypto_co_create_generic(BlockDriverState *bs,
+ int64_t size,
+ QCryptoBlockCreateOptions *opts,
+ Error **errp)
{
- int ret = -EINVAL;
- QCryptoBlockCreateOptions *create_opts = NULL;
+ int ret;
+ BlockBackend *blk;
QCryptoBlock *crypto = NULL;
- struct BlockCryptoCreateData data = {
- .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE),
- .opts = opts,
- .filename = filename,
- };
- QDict *cryptoopts;
+ struct BlockCryptoCreateData data;
- cryptoopts = qemu_opts_to_qdict(opts, NULL);
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
- create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
- if (!create_opts) {
- return -1;
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto cleanup;
}
- crypto = qcrypto_block_create(create_opts, NULL,
+ data = (struct BlockCryptoCreateData) {
+ .blk = blk,
+ .size = size,
+ };
+
+ crypto = qcrypto_block_create(opts, NULL,
block_crypto_init_func,
block_crypto_write_func,
&data,
@@ -358,10 +346,8 @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
ret = 0;
cleanup:
- QDECREF(cryptoopts);
qcrypto_block_free(crypto);
- blk_unref(data.blk);
- qapi_free_QCryptoBlockCreateOptions(create_opts);
+ blk_unref(blk);
return ret;
}
@@ -537,7 +523,10 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
assert(offset < INT64_MAX);
- assert(offset < len);
+
+ if (offset > len) {
+ return -EIO;
+ }
len -= offset;
@@ -562,12 +551,88 @@ static int block_crypto_open_luks(BlockDriverState *bs,
bs, options, flags, errp);
}
+static int coroutine_fn
+block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
+{
+ BlockdevCreateOptionsLUKS *luks_opts;
+ BlockDriverState *bs = NULL;
+ QCryptoBlockCreateOptions create_opts;
+ int ret;
+
+ assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
+ luks_opts = &create_options->u.luks;
+
+ bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
+ if (bs == NULL) {
+ return -EIO;
+ }
+
+ create_opts = (QCryptoBlockCreateOptions) {
+ .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
+ .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
+ };
+
+ ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
+ errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ bdrv_unref(bs);
+ return ret;
+}
+
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
QemuOpts *opts,
Error **errp)
{
- return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
- filename, opts, errp);
+ QCryptoBlockCreateOptions *create_opts = NULL;
+ BlockDriverState *bs = NULL;
+ QDict *cryptoopts;
+ int64_t size;
+ int ret;
+
+ /* Parse options */
+ size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
+
+ cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
+ &block_crypto_create_opts_luks,
+ true);
+
+ create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS,
+ cryptoopts, errp);
+ if (!create_opts) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Create protocol layer */
+ ret = bdrv_create_file(filename, opts, errp);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (!bs) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Create format layer */
+ ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ bdrv_unref(bs);
+ qapi_free_QCryptoBlockCreateOptions(create_opts);
+ QDECREF(cryptoopts);
+ return ret;
}
static int block_crypto_get_info_luks(BlockDriverState *bs,
@@ -623,6 +688,7 @@ BlockDriver bdrv_crypto_luks = {
.bdrv_open = block_crypto_open_luks,
.bdrv_close = block_crypto_close,
.bdrv_child_perm = bdrv_format_default_perms,
+ .bdrv_co_create = block_crypto_co_create_luks,
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
.bdrv_truncate = block_crypto_truncate,
.create_opts = &block_crypto_create_opts_luks,
diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c
index 909f0517f8..967159479d 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -40,6 +40,8 @@ struct BdrvDirtyBitmap {
QemuMutex *mutex;
HBitmap *bitmap; /* Dirty bitmap implementation */
HBitmap *meta; /* Meta dirty bitmap */
+ bool qmp_locked; /* Bitmap is locked, it can't be modified
+ through QMP */
BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */
char *name; /* Optional non-empty unique ID */
int64_t size; /* Size of the bitmap, in bytes */
@@ -183,6 +185,18 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
return bitmap->successor;
}
+void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
+{
+ qemu_mutex_lock(bitmap->mutex);
+ bitmap->qmp_locked = qmp_locked;
+ qemu_mutex_unlock(bitmap->mutex);
+}
+
+bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap)
+{
+ return bitmap->qmp_locked;
+}
+
/* Called with BQL taken. */
bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap)
{
@@ -194,6 +208,8 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap)
{
if (bdrv_dirty_bitmap_frozen(bitmap)) {
return DIRTY_BITMAP_STATUS_FROZEN;
+ } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
+ return DIRTY_BITMAP_STATUS_LOCKED;
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
return DIRTY_BITMAP_STATUS_DISABLED;
} else {
@@ -234,6 +250,59 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
return 0;
}
+/* Called with BQL taken. */
+void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
+{
+ qemu_mutex_lock(bitmap->mutex);
+ bdrv_enable_dirty_bitmap(bitmap->successor);
+ qemu_mutex_unlock(bitmap->mutex);
+}
+
+/* Called within bdrv_dirty_bitmap_lock..unlock */
+static void bdrv_do_release_matching_dirty_bitmap_locked(
+ BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
+ bool (*cond)(BdrvDirtyBitmap *bitmap))
+{
+ BdrvDirtyBitmap *bm, *next;
+
+ QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
+ if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
+ assert(!bm->active_iterators);
+ assert(!bdrv_dirty_bitmap_frozen(bm));
+ assert(!bm->meta);
+ QLIST_REMOVE(bm, list);
+ hbitmap_free(bm->bitmap);
+ g_free(bm->name);
+ g_free(bm);
+
+ if (bitmap) {
+ return;
+ }
+ }
+ }
+
+ if (bitmap) {
+ abort();
+ }
+}
+
+/* Called with BQL taken. */
+static void bdrv_do_release_matching_dirty_bitmap(
+ BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
+ bool (*cond)(BdrvDirtyBitmap *bitmap))
+{
+ bdrv_dirty_bitmaps_lock(bs);
+ bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond);
+ bdrv_dirty_bitmaps_unlock(bs);
+}
+
+/* Called within bdrv_dirty_bitmap_lock..unlock */
+static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap)
+{
+ bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL);
+}
+
/**
* For a bitmap with a successor, yield our name to the successor,
* delete the old bitmap, and return a handle to the new bitmap.
@@ -267,11 +336,11 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
* In cases of failure where we can no longer safely delete the parent,
* we may wish to re-join the parent and child/successor.
* The merged parent will be un-frozen, but not explicitly re-enabled.
- * Called with BQL taken.
+ * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken.
*/
-BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
- BdrvDirtyBitmap *parent,
- Error **errp)
+BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
+ BdrvDirtyBitmap *parent,
+ Error **errp)
{
BdrvDirtyBitmap *successor = parent->successor;
@@ -284,12 +353,26 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
error_setg(errp, "Merging of parent and successor bitmap failed");
return NULL;
}
- bdrv_release_dirty_bitmap(bs, successor);
+ bdrv_release_dirty_bitmap_locked(bs, successor);
parent->successor = NULL;
return parent;
}
+/* Called with BQL taken. */
+BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
+ BdrvDirtyBitmap *parent,
+ Error **errp)
+{
+ BdrvDirtyBitmap *ret;
+
+ qemu_mutex_lock(parent->mutex);
+ ret = bdrv_reclaim_dirty_bitmap_locked(bs, parent, errp);
+ qemu_mutex_unlock(parent->mutex);
+
+ return ret;
+}
+
/**
* Truncates _all_ bitmaps attached to a BDS.
* Called with BQL taken.
@@ -314,36 +397,6 @@ static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
}
/* Called with BQL taken. */
-static void bdrv_do_release_matching_dirty_bitmap(
- BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
- bool (*cond)(BdrvDirtyBitmap *bitmap))
-{
- BdrvDirtyBitmap *bm, *next;
- bdrv_dirty_bitmaps_lock(bs);
- QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
- if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
- assert(!bm->active_iterators);
- assert(!bdrv_dirty_bitmap_frozen(bm));
- assert(!bm->meta);
- QLIST_REMOVE(bm, list);
- hbitmap_free(bm->bitmap);
- g_free(bm->name);
- g_free(bm);
-
- if (bitmap) {
- goto out;
- }
- }
- }
- if (bitmap) {
- abort();
- }
-
-out:
- bdrv_dirty_bitmaps_unlock(bs);
-}
-
-/* Called with BQL taken. */
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
{
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL);
diff --git a/block/gluster.c b/block/gluster.c
index 63d3c37d4c..296e036b3d 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -665,7 +665,7 @@ static int qemu_gluster_parse(BlockdevOptionsGluster *gconf,
if (filename) {
ret = qemu_gluster_parse_uri(gconf, filename);
if (ret < 0) {
- error_setg(errp, "invalid URI");
+ error_setg(errp, "invalid URI %s", filename);
error_append_hint(errp, "Usage: file=gluster[+transport]://"
"[host[:port]]volume/path[?socket=...]"
"[,file.debug=N]"
diff --git a/block/iscsi.c b/block/iscsi.c
index a82170f16e..f5aecfc883 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2244,7 +2244,7 @@ static BlockDriver bdrv_iser = {
.create_opts = &iscsi_create_opts,
.bdrv_reopen_prepare = iscsi_reopen_prepare,
.bdrv_reopen_commit = iscsi_reopen_commit,
- .bdrv_invalidate_cache = iscsi_invalidate_cache,
+ .bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
.bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info,
diff --git a/block/mirror.c b/block/mirror.c
index f5bf620942..820f512c7b 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -869,11 +869,8 @@ static void coroutine_fn mirror_run(void *opaque)
ret = 0;
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
- if (!s->synced) {
- block_job_sleep_ns(&s->common, delay_ns);
- if (block_job_is_cancelled(&s->common)) {
- break;
- }
+ if (block_job_is_cancelled(&s->common) && s->common.force) {
+ break;
} else if (!should_complete) {
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
block_job_sleep_ns(&s->common, delay_ns);
@@ -887,7 +884,8 @@ immediate_exit:
* or it was cancelled prematurely so that we do not guarantee that
* the target is a copy of the source.
*/
- assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
+ assert(ret < 0 || ((s->common.force || !s->synced) &&
+ block_job_is_cancelled(&s->common)));
assert(need_drain);
mirror_wait_for_all_io(s);
}
@@ -1166,7 +1164,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
}
/* Make sure that the source is not resized while the job is running */
- s = block_job_create(job_id, driver, mirror_top_bs,
+ s = block_job_create(job_id, driver, NULL, mirror_top_bs,
BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
diff --git a/block/nbd-client.c b/block/nbd-client.c
index 7b68499b76..e64e346d69 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -228,6 +228,48 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk,
return 0;
}
+/* nbd_parse_blockstatus_payload
+ * support only one extent in reply and only for
+ * base:allocation context
+ */
+static int nbd_parse_blockstatus_payload(NBDClientSession *client,
+ NBDStructuredReplyChunk *chunk,
+ uint8_t *payload, uint64_t orig_length,
+ NBDExtent *extent, Error **errp)
+{
+ uint32_t context_id;
+
+ if (chunk->length != sizeof(context_id) + sizeof(extent)) {
+ error_setg(errp, "Protocol error: invalid payload for "
+ "NBD_REPLY_TYPE_BLOCK_STATUS");
+ return -EINVAL;
+ }
+
+ context_id = payload_advance32(&payload);
+ if (client->info.meta_base_allocation_id != context_id) {
+ error_setg(errp, "Protocol error: unexpected context id %d for "
+ "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context "
+ "id is %d", context_id,
+ client->info.meta_base_allocation_id);
+ return -EINVAL;
+ }
+
+ extent->length = payload_advance32(&payload);
+ extent->flags = payload_advance32(&payload);
+
+ if (extent->length == 0 ||
+ (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
+ client->info.min_block)) ||
+ extent->length > orig_length)
+ {
+ error_setg(errp, "Protocol error: server sent status chunk with "
+ "invalid length");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/* nbd_parse_error_payload
* on success @errp contains message describing nbd error reply
*/
@@ -481,6 +523,7 @@ static coroutine_fn int nbd_co_receive_one_chunk(
typedef struct NBDReplyChunkIter {
int ret;
+ bool fatal;
Error *err;
bool done, only_structured;
} NBDReplyChunkIter;
@@ -490,11 +533,12 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal,
{
assert(ret < 0);
- if (fatal || iter->ret == 0) {
+ if ((fatal && !iter->fatal) || iter->ret == 0) {
if (iter->ret != 0) {
error_free(iter->err);
iter->err = NULL;
}
+ iter->fatal = fatal;
iter->ret = ret;
error_propagate(&iter->err, *local_err);
} else {
@@ -640,6 +684,68 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle,
return iter.ret;
}
+static int nbd_co_receive_blockstatus_reply(NBDClientSession *s,
+ uint64_t handle, uint64_t length,
+ NBDExtent *extent, Error **errp)
+{
+ NBDReplyChunkIter iter;
+ NBDReply reply;
+ void *payload = NULL;
+ Error *local_err = NULL;
+ bool received = false;
+
+ assert(!extent->length);
+ NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply,
+ NULL, &reply, &payload)
+ {
+ int ret;
+ NBDStructuredReplyChunk *chunk = &reply.structured;
+
+ assert(nbd_reply_is_structured(&reply));
+
+ switch (chunk->type) {
+ case NBD_REPLY_TYPE_BLOCK_STATUS:
+ if (received) {
+ s->quit = true;
+ error_setg(&local_err, "Several BLOCK_STATUS chunks in reply");
+ nbd_iter_error(&iter, true, -EINVAL, &local_err);
+ }
+ received = true;
+
+ ret = nbd_parse_blockstatus_payload(s, &reply.structured,
+ payload, length, extent,
+ &local_err);
+ if (ret < 0) {
+ s->quit = true;
+ nbd_iter_error(&iter, true, ret, &local_err);
+ }
+ break;
+ default:
+ if (!nbd_reply_type_is_error(chunk->type)) {
+ s->quit = true;
+ error_setg(&local_err,
+ "Unexpected reply type: %d (%s) "
+ "for CMD_BLOCK_STATUS",
+ chunk->type, nbd_reply_type_lookup(chunk->type));
+ nbd_iter_error(&iter, true, -EINVAL, &local_err);
+ }
+ }
+
+ g_free(payload);
+ payload = NULL;
+ }
+
+ if (!extent->length && !iter.err) {
+ error_setg(&iter.err,
+ "Server did not reply with any status extents");
+ if (!iter.ret) {
+ iter.ret = -EIO;
+ }
+ }
+ error_propagate(errp, iter.err);
+ return iter.ret;
+}
+
static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
QEMUIOVector *write_qiov)
{
@@ -782,6 +888,51 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes)
return nbd_co_request(bs, &request, NULL);
}
+int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset, int64_t bytes,
+ int64_t *pnum, int64_t *map,
+ BlockDriverState **file)
+{
+ int64_t ret;
+ NBDExtent extent = { 0 };
+ NBDClientSession *client = nbd_get_client_session(bs);
+ Error *local_err = NULL;
+
+ NBDRequest request = {
+ .type = NBD_CMD_BLOCK_STATUS,
+ .from = offset,
+ .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX,
+ bs->bl.request_alignment),
+ client->info.max_block), bytes),
+ .flags = NBD_CMD_FLAG_REQ_ONE,
+ };
+
+ if (!client->info.base_allocation) {
+ *pnum = bytes;
+ return BDRV_BLOCK_DATA;
+ }
+
+ ret = nbd_co_send_request(bs, &request, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes,
+ &extent, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ if (ret < 0) {
+ return ret;
+ }
+
+ assert(extent.length);
+ *pnum = extent.length;
+ return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) |
+ (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0);
+}
+
void nbd_client_detach_aio_context(BlockDriverState *bs)
{
NBDClientSession *client = nbd_get_client_session(bs);
@@ -826,6 +977,7 @@ int nbd_client_init(BlockDriverState *bs,
client->info.request_sizes = true;
client->info.structured_reply = true;
+ client->info.base_allocation = true;
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
tlscreds, hostname,
&client->ioc, &client->info, errp);
diff --git a/block/nbd-client.h b/block/nbd-client.h
index 612c4c21a0..0ece76e5af 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -61,4 +61,10 @@ void nbd_client_detach_aio_context(BlockDriverState *bs);
void nbd_client_attach_aio_context(BlockDriverState *bs,
AioContext *new_context);
+int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs,
+ bool want_zero,
+ int64_t offset, int64_t bytes,
+ int64_t *pnum, int64_t *map,
+ BlockDriverState **file);
+
#endif /* NBD_CLIENT_H */
diff --git a/block/nbd.c b/block/nbd.c
index d4e4172c08..1e2b3ba2d3 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -585,6 +585,7 @@ static BlockDriver bdrv_nbd = {
.bdrv_detach_aio_context = nbd_detach_aio_context,
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
+ .bdrv_co_block_status = nbd_client_co_block_status,
};
static BlockDriver bdrv_nbd_tcp = {
@@ -604,6 +605,7 @@ static BlockDriver bdrv_nbd_tcp = {
.bdrv_detach_aio_context = nbd_detach_aio_context,
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
+ .bdrv_co_block_status = nbd_client_co_block_status,
};
static BlockDriver bdrv_nbd_unix = {
@@ -623,6 +625,7 @@ static BlockDriver bdrv_nbd_unix = {
.bdrv_detach_aio_context = nbd_detach_aio_context,
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
+ .bdrv_co_block_status = nbd_client_co_block_status,
};
static void bdrv_nbd_init(void)
diff --git a/block/parallels.c b/block/parallels.c
index c13cb619e6..e2515dec81 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -34,6 +34,9 @@
#include "sysemu/block-backend.h"
#include "qemu/module.h"
#include "qemu/option.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "qemu/bswap.h"
#include "qemu/bitmap.h"
#include "migration/blocker.h"
@@ -79,6 +82,25 @@ static QemuOptsList parallels_runtime_opts = {
},
};
+static QemuOptsList parallels_create_opts = {
+ .name = "parallels-create-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
+ .desc = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Virtual disk size",
+ },
+ {
+ .name = BLOCK_OPT_CLUSTER_SIZE,
+ .type = QEMU_OPT_SIZE,
+ .help = "Parallels image cluster size",
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
+ },
+ { /* end of list */ }
+ }
+};
+
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
{
@@ -480,46 +502,62 @@ out:
}
-static int coroutine_fn parallels_co_create_opts(const char *filename,
- QemuOpts *opts,
- Error **errp)
+static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
+ Error **errp)
{
+ BlockdevCreateOptionsParallels *parallels_opts;
+ BlockDriverState *bs;
+ BlockBackend *blk;
int64_t total_size, cl_size;
- uint8_t tmp[BDRV_SECTOR_SIZE];
- Error *local_err = NULL;
- BlockBackend *file;
uint32_t bat_entries, bat_sectors;
ParallelsHeader header;
+ uint8_t tmp[BDRV_SECTOR_SIZE];
int ret;
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
- DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
+ assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
+ parallels_opts = &opts->u.parallels;
+
+ /* Sanity checks */
+ total_size = parallels_opts->size;
+
+ if (parallels_opts->has_cluster_size) {
+ cl_size = parallels_opts->cluster_size;
+ } else {
+ cl_size = DEFAULT_CLUSTER_SIZE;
+ }
+
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
- error_propagate(errp, local_err);
+ error_setg(errp, "Image size is too large for this cluster size");
return -E2BIG;
}
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- return ret;
+ if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
+ return -EINVAL;
}
- file = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (file == NULL) {
- error_propagate(errp, local_err);
+ if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
+ error_setg(errp, "Cluster size must be a multiple of 512 bytes");
+ return -EINVAL;
+ }
+
+ /* Create BlockBackend to write to the image */
+ bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
+ if (bs == NULL) {
return -EIO;
}
- blk_set_allow_write_beyond_eof(file, true);
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ blk_set_allow_write_beyond_eof(blk, true);
- ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
+ /* Create image format */
+ ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
- goto exit;
+ goto out;
}
bat_entries = DIV_ROUND_UP(total_size, cl_size);
@@ -542,24 +580,107 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
memset(tmp, 0, sizeof(tmp));
memcpy(tmp, &header, sizeof(header));
- ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0);
+ ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0);
if (ret < 0) {
goto exit;
}
- ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE,
+ ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
if (ret < 0) {
goto exit;
}
- ret = 0;
-done:
- blk_unref(file);
+ ret = 0;
+out:
+ blk_unref(blk);
+ bdrv_unref(bs);
return ret;
exit:
error_setg_errno(errp, -ret, "Failed to create Parallels image");
- goto done;
+ goto out;
+}
+
+static int coroutine_fn parallels_co_create_opts(const char *filename,
+ QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ Error *local_err = NULL;
+ BlockDriverState *bs = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ int ret;
+
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
+ { NULL, NULL },
+ };
+
+ /* Parse options and convert legacy syntax */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &parallels_create_opts,
+ true);
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto done;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto done;
+ }
+
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qdict_put_str(qdict, "driver", "parallels");
+ qdict_put_str(qdict, "file", bs->node_name);
+
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to(QDict, qobj);
+ if (qdict == NULL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Silently round up sizes */
+ create_options->u.parallels.size =
+ ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
+ create_options->u.parallels.cluster_size =
+ ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
+
+ /* Create the Parallels image (format layer) */
+ ret = parallels_co_create(create_options, errp);
+ if (ret < 0) {
+ goto done;
+ }
+ ret = 0;
+
+done:
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
+ return ret;
}
@@ -771,25 +892,6 @@ static void parallels_close(BlockDriverState *bs)
error_free(s->migration_blocker);
}
-static QemuOptsList parallels_create_opts = {
- .name = "parallels-create-opts",
- .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
- .desc = {
- {
- .name = BLOCK_OPT_SIZE,
- .type = QEMU_OPT_SIZE,
- .help = "Virtual disk size",
- },
- {
- .name = BLOCK_OPT_CLUSTER_SIZE,
- .type = QEMU_OPT_SIZE,
- .help = "Parallels image cluster size",
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
- },
- { /* end of list */ }
- }
-};
-
static BlockDriver bdrv_parallels = {
.format_name = "parallels",
.instance_size = sizeof(BDRVParallelsState),
@@ -803,6 +905,7 @@ static BlockDriver bdrv_parallels = {
.bdrv_co_readv = parallels_co_readv,
.bdrv_co_writev = parallels_co_writev,
.supports_backing = true,
+ .bdrv_co_create = parallels_co_create,
.bdrv_co_create_opts = parallels_co_create_opts,
.bdrv_co_check = parallels_co_check,
.create_opts = &parallels_create_opts,
diff --git a/block/qapi.c b/block/qapi.c
index 4c9923d262..04c6fc69b9 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -394,6 +394,37 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
qapi_free_BlockInfo(info);
}
+static uint64List *uint64_list(uint64_t *list, int size)
+{
+ int i;
+ uint64List *out_list = NULL;
+ uint64List **pout_list = &out_list;
+
+ for (i = 0; i < size; i++) {
+ uint64List *entry = g_new(uint64List, 1);
+ entry->value = list[i];
+ *pout_list = entry;
+ pout_list = &entry->next;
+ }
+
+ *pout_list = NULL;
+
+ return out_list;
+}
+
+static void bdrv_latency_histogram_stats(BlockLatencyHistogram *hist,
+ bool *not_null,
+ BlockLatencyHistogramInfo **info)
+{
+ *not_null = hist->bins != NULL;
+ if (*not_null) {
+ *info = g_new0(BlockLatencyHistogramInfo, 1);
+
+ (*info)->boundaries = uint64_list(hist->boundaries, hist->nbins - 1);
+ (*info)->bins = uint64_list(hist->bins, hist->nbins);
+ }
+}
+
static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
{
BlockAcctStats *stats = blk_get_stats(blk);
@@ -459,6 +490,16 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
dev_stats->avg_wr_queue_depth =
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
}
+
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ],
+ &ds->has_x_rd_latency_histogram,
+ &ds->x_rd_latency_histogram);
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE],
+ &ds->has_x_wr_latency_histogram,
+ &ds->x_wr_latency_histogram);
+ bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH],
+ &ds->has_x_flush_latency_histogram,
+ &ds->x_flush_latency_histogram);
}
static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
@@ -647,29 +688,29 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
{
switch (qobject_type(obj)) {
case QTYPE_QNUM: {
- QNum *value = qobject_to_qnum(obj);
+ QNum *value = qobject_to(QNum, obj);
char *tmp = qnum_to_string(value);
func_fprintf(f, "%s", tmp);
g_free(tmp);
break;
}
case QTYPE_QSTRING: {
- QString *value = qobject_to_qstring(obj);
+ QString *value = qobject_to(QString, obj);
func_fprintf(f, "%s", qstring_get_str(value));
break;
}
case QTYPE_QDICT: {
- QDict *value = qobject_to_qdict(obj);
+ QDict *value = qobject_to(QDict, obj);
dump_qdict(func_fprintf, f, comp_indent, value);
break;
}
case QTYPE_QLIST: {
- QList *value = qobject_to_qlist(obj);
+ QList *value = qobject_to(QList, obj);
dump_qlist(func_fprintf, f, comp_indent, value);
break;
}
case QTYPE_QBOOL: {
- QBool *value = qobject_to_qbool(obj);
+ QBool *value = qobject_to(QBool, obj);
func_fprintf(f, "%s", qbool_get_bool(value) ? "true" : "false");
break;
}
@@ -730,7 +771,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
visit_complete(v, &obj);
- data = qdict_get(qobject_to_qdict(obj), "data");
+ data = qdict_get(qobject_to(QDict, obj), "data");
dump_qobject(func_fprintf, f, 1, data);
qobject_decref(obj);
visit_free(v);
diff --git a/block/qcow.c b/block/qcow.c
index 47a18d9a3a..f92891676c 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -33,6 +33,8 @@
#include <zlib.h>
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "crypto/block.h"
#include "migration/blocker.h"
#include "block/crypto.h"
@@ -86,6 +88,8 @@ typedef struct BDRVQcowState {
Error *migration_blocker;
} BDRVQcowState;
+static QemuOptsList qcow_create_opts;
+
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
@@ -810,62 +814,50 @@ static void qcow_close(BlockDriverState *bs)
error_free(s->migration_blocker);
}
-static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
+ Error **errp)
{
+ BlockdevCreateOptionsQcow *qcow_opts;
int header_size, backing_filename_len, l1_size, shift, i;
QCowHeader header;
uint8_t *tmp;
int64_t total_size = 0;
- char *backing_file = NULL;
- Error *local_err = NULL;
int ret;
+ BlockDriverState *bs;
BlockBackend *qcow_blk;
- char *encryptfmt = NULL;
- QDict *options;
- QDict *encryptopts = NULL;
- QCryptoBlockCreateOptions *crypto_opts = NULL;
QCryptoBlock *crypto = NULL;
- /* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
+ assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
+ qcow_opts = &opts->u.qcow;
+
+ /* Sanity checks */
+ total_size = qcow_opts->size;
if (total_size == 0) {
error_setg(errp, "Image size is too small, cannot be zero length");
- ret = -EINVAL;
- goto cleanup;
+ return -EINVAL;
}
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
- if (encryptfmt) {
- if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
- error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
- BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
- ret = -EINVAL;
- goto cleanup;
- }
- } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
- encryptfmt = g_strdup("aes");
+ if (qcow_opts->has_encrypt &&
+ qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
+ {
+ error_setg(errp, "Unsupported encryption format");
+ return -EINVAL;
}
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- goto cleanup;
+ /* Create BlockBackend to write to the image */
+ bs = bdrv_open_blockdev_ref(qcow_opts->file, errp);
+ if (bs == NULL) {
+ return -EIO;
}
- qcow_blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (qcow_blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
- goto cleanup;
+ qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(qcow_blk, bs, errp);
+ if (ret < 0) {
+ goto exit;
}
-
blk_set_allow_write_beyond_eof(qcow_blk, true);
+ /* Create image format */
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto exit;
@@ -877,16 +869,15 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
header.size = cpu_to_be64(total_size);
header_size = sizeof(header);
backing_filename_len = 0;
- if (backing_file) {
- if (strcmp(backing_file, "fat:")) {
+ if (qcow_opts->has_backing_file) {
+ if (strcmp(qcow_opts->backing_file, "fat:")) {
header.backing_file_offset = cpu_to_be64(header_size);
- backing_filename_len = strlen(backing_file);
+ backing_filename_len = strlen(qcow_opts->backing_file);
header.backing_file_size = cpu_to_be32(backing_filename_len);
header_size += backing_filename_len;
} else {
/* special backing file for vvfat */
- g_free(backing_file);
- backing_file = NULL;
+ qcow_opts->has_backing_file = false;
}
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
unmodified sectors */
@@ -901,26 +892,10 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
header.l1_table_offset = cpu_to_be64(header_size);
- options = qemu_opts_to_qdict(opts, NULL);
- qdict_extract_subqdict(options, &encryptopts, "encrypt.");
- QDECREF(options);
- if (encryptfmt) {
- if (!g_str_equal(encryptfmt, "aes")) {
- error_setg(errp, "Unknown encryption format '%s', expected 'aes'",
- encryptfmt);
- ret = -EINVAL;
- goto exit;
- }
+ if (qcow_opts->has_encrypt) {
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
- crypto_opts = block_crypto_create_opts_init(
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
- if (!crypto_opts) {
- ret = -EINVAL;
- goto exit;
- }
-
- crypto = qcrypto_block_create(crypto_opts, "encrypt.",
+ crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.",
NULL, NULL, NULL, errp);
if (!crypto) {
ret = -EINVAL;
@@ -936,9 +911,9 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
goto exit;
}
- if (backing_file) {
+ if (qcow_opts->has_backing_file) {
ret = blk_pwrite(qcow_blk, sizeof(header),
- backing_file, backing_filename_len, 0);
+ qcow_opts->backing_file, backing_filename_len, 0);
if (ret != backing_filename_len) {
goto exit;
}
@@ -959,12 +934,100 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
ret = 0;
exit:
blk_unref(qcow_blk);
-cleanup:
- QDECREF(encryptopts);
- g_free(encryptfmt);
qcrypto_block_free(crypto);
- qapi_free_QCryptoBlockCreateOptions(crypto_opts);
- g_free(backing_file);
+ return ret;
+}
+
+static int coroutine_fn qcow_co_create_opts(const char *filename,
+ QemuOpts *opts, Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ BlockDriverState *bs = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ const char *val;
+ Error *local_err = NULL;
+ int ret;
+
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
+ { NULL, NULL },
+ };
+
+ /* Parse options and convert legacy syntax */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
+
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
+ if (val && !strcmp(val, "on")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
+ } else if (val && !strcmp(val, "off")) {
+ qdict_del(qdict, BLOCK_OPT_ENCRYPT);
+ }
+
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
+ if (val && !strcmp(val, "aes")) {
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
+ }
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qdict_put_str(qdict, "driver", "qcow");
+ qdict_put_str(qdict, "file", bs->node_name);
+
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to(QDict, qobj);
+ if (qdict == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Silently round up size */
+ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW);
+ create_options->u.qcow.size =
+ ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE);
+
+ /* Create the qcow image (format layer) */
+ ret = qcow_co_create(create_options, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -1128,6 +1191,7 @@ static BlockDriver bdrv_qcow = {
.bdrv_close = qcow_close,
.bdrv_child_perm = bdrv_format_default_perms,
.bdrv_reopen_prepare = qcow_reopen_prepare,
+ .bdrv_co_create = qcow_co_create,
.bdrv_co_create_opts = qcow_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.supports_backing = true,
diff --git a/block/qcow2.c b/block/qcow2.c
index 7472af6931..cf4f3becae 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3125,7 +3125,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
/* Now get the QAPI type BlockdevCreateOptions */
qobj = qdict_crumple(qdict, errp);
QDECREF(qdict);
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL;
goto finish;
diff --git a/block/qed.c b/block/qed.c
index 5e6a6bfaa0..35ff505066 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -20,6 +20,11 @@
#include "trace.h"
#include "qed.h"
#include "sysemu/block-backend.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
+
+static QemuOptsList qed_create_opts;
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
const char *filename)
@@ -594,57 +599,95 @@ static void bdrv_qed_close(BlockDriverState *bs)
qemu_vfree(s->l1_table);
}
-static int qed_create(const char *filename, uint32_t cluster_size,
- uint64_t image_size, uint32_t table_size,
- const char *backing_file, const char *backing_fmt,
- QemuOpts *opts, Error **errp)
+static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
+ Error **errp)
{
- QEDHeader header = {
- .magic = QED_MAGIC,
- .cluster_size = cluster_size,
- .table_size = table_size,
- .header_size = 1,
- .features = 0,
- .compat_features = 0,
- .l1_table_offset = cluster_size,
- .image_size = image_size,
- };
+ BlockdevCreateOptionsQed *qed_opts;
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
+
+ QEDHeader header;
QEDHeader le_header;
uint8_t *l1_table = NULL;
- size_t l1_size = header.cluster_size * header.table_size;
- Error *local_err = NULL;
+ size_t l1_size;
int ret = 0;
- BlockBackend *blk;
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- return ret;
+ assert(opts->driver == BLOCKDEV_DRIVER_QED);
+ qed_opts = &opts->u.qed;
+
+ /* Validate options and set default values */
+ if (!qed_opts->has_cluster_size) {
+ qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE;
+ }
+ if (!qed_opts->has_table_size) {
+ qed_opts->table_size = QED_DEFAULT_TABLE_SIZE;
}
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
+ if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) {
+ error_setg(errp, "QED cluster size must be within range [%u, %u] "
+ "and power of 2",
+ QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
+ return -EINVAL;
+ }
+ if (!qed_is_table_size_valid(qed_opts->table_size)) {
+ error_setg(errp, "QED table size must be within range [%u, %u] "
+ "and power of 2",
+ QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
+ return -EINVAL;
+ }
+ if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size,
+ qed_opts->table_size))
+ {
+ error_setg(errp, "QED image size must be a non-zero multiple of "
+ "cluster size and less than %" PRIu64 " bytes",
+ qed_max_image_size(qed_opts->cluster_size,
+ qed_opts->table_size));
+ return -EINVAL;
+ }
+
+ /* Create BlockBackend to write to the image */
+ bs = bdrv_open_blockdev_ref(qed_opts->file, errp);
+ if (bs == NULL) {
return -EIO;
}
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
blk_set_allow_write_beyond_eof(blk, true);
+ /* Prepare image format */
+ header = (QEDHeader) {
+ .magic = QED_MAGIC,
+ .cluster_size = qed_opts->cluster_size,
+ .table_size = qed_opts->table_size,
+ .header_size = 1,
+ .features = 0,
+ .compat_features = 0,
+ .l1_table_offset = qed_opts->cluster_size,
+ .image_size = qed_opts->size,
+ };
+
+ l1_size = header.cluster_size * header.table_size;
+
/* File must start empty and grow, check truncate is supported */
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
if (ret < 0) {
goto out;
}
- if (backing_file) {
+ if (qed_opts->has_backing_file) {
header.features |= QED_F_BACKING_FILE;
header.backing_filename_offset = sizeof(le_header);
- header.backing_filename_size = strlen(backing_file);
+ header.backing_filename_size = strlen(qed_opts->backing_file);
- if (qed_fmt_is_raw(backing_fmt)) {
- header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
+ if (qed_opts->has_backing_fmt) {
+ const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt);
+ if (qed_fmt_is_raw(backing_fmt)) {
+ header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
+ }
}
}
@@ -653,7 +696,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
if (ret < 0) {
goto out;
}
- ret = blk_pwrite(blk, sizeof(le_header), backing_file,
+ ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file,
header.backing_filename_size, 0);
if (ret < 0) {
goto out;
@@ -669,6 +712,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
out:
g_free(l1_table);
blk_unref(blk);
+ bdrv_unref(bs);
return ret;
}
@@ -676,51 +720,78 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
QemuOpts *opts,
Error **errp)
{
- uint64_t image_size = 0;
- uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
- uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
- char *backing_file = NULL;
- char *backing_fmt = NULL;
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ BlockDriverState *bs = NULL;
+ Error *local_err = NULL;
int ret;
- image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
- backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
- cluster_size = qemu_opt_get_size_del(opts,
- BLOCK_OPT_CLUSTER_SIZE,
- QED_DEFAULT_CLUSTER_SIZE);
- table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
- QED_DEFAULT_TABLE_SIZE);
-
- if (!qed_is_cluster_size_valid(cluster_size)) {
- error_setg(errp, "QED cluster size must be within range [%u, %u] "
- "and power of 2",
- QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
+ static const QDictRenames opt_renames[] = {
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
+ { BLOCK_OPT_BACKING_FMT, "backing-fmt" },
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
+ { BLOCK_OPT_TABLE_SIZE, "table-size" },
+ { NULL, NULL },
+ };
+
+ /* Parse options and convert legacy syntax */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
ret = -EINVAL;
- goto finish;
+ goto fail;
}
- if (!qed_is_table_size_valid(table_size)) {
- error_setg(errp, "QED table size must be within range [%u, %u] "
- "and power of 2",
- QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qdict_put_str(qdict, "driver", "qed");
+ qdict_put_str(qdict, "file", bs->node_name);
+
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to(QDict, qobj);
+ if (qdict == NULL) {
ret = -EINVAL;
- goto finish;
+ goto fail;
}
- if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
- error_setg(errp, "QED image size must be a non-zero multiple of "
- "cluster size and less than %" PRIu64 " bytes",
- qed_max_image_size(cluster_size, table_size));
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
- goto finish;
+ goto fail;
}
- ret = qed_create(filename, cluster_size, image_size, table_size,
- backing_file, backing_fmt, opts, errp);
+ /* Silently round up size */
+ assert(create_options->driver == BLOCKDEV_DRIVER_QED);
+ create_options->u.qed.size =
+ ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
+
+ /* Create the qed image (format layer) */
+ ret = bdrv_qed_co_create(create_options, errp);
-finish:
- g_free(backing_file);
- g_free(backing_fmt);
+fail:
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
@@ -1602,6 +1673,7 @@ static BlockDriver bdrv_qed = {
.bdrv_close = bdrv_qed_close,
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
.bdrv_child_perm = bdrv_format_default_perms,
+ .bdrv_co_create = bdrv_qed_co_create,
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_block_status = bdrv_qed_co_block_status,
diff --git a/block/rbd.c b/block/rbd.c
index 294ed07ac4..5b64849dc6 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -263,14 +263,14 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
if (!keypairs_json) {
return ret;
}
- keypairs = qobject_to_qlist(qobject_from_json(keypairs_json,
- &error_abort));
+ keypairs = qobject_to(QList,
+ qobject_from_json(keypairs_json, &error_abort));
remaining = qlist_size(keypairs) / 2;
assert(remaining);
while (remaining--) {
- name = qobject_to_qstring(qlist_pop(keypairs));
- value = qobject_to_qstring(qlist_pop(keypairs));
+ name = qobject_to(QString, qlist_pop(keypairs));
+ value = qobject_to(QString, qlist_pop(keypairs));
assert(name && value);
key = qstring_get_str(name);
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 8680b2926f..387f59c8aa 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1036,7 +1036,7 @@ static void sd_parse_uri(SheepdogConfig *cfg, const char *filename,
cfg->uri = uri = uri_parse(filename);
if (!uri) {
- error_setg(&err, "invalid URI");
+ error_setg(&err, "invalid URI '%s'", filename);
goto out;
}
@@ -1887,7 +1887,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
return -EINVAL;
}
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
qdict_put_str(qdict, "driver", "sheepdog");
diff --git a/block/stream.c b/block/stream.c
index 499cdacdb0..f3b53f49e2 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -244,7 +244,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
/* Prevent concurrent jobs trying to modify the graph structure here, we
* already have our own plans. Also don't allow resize as the image size is
* queried only at the job start and then cached. */
- s = block_job_create(job_id, &stream_job_driver, bs,
+ s = block_job_create(job_id, &stream_job_driver, NULL, bs,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
BLK_PERM_GRAPH_MOD,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
diff --git a/block/trace-events b/block/trace-events
index 02dd80ff0c..f8c50b4063 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -4,9 +4,16 @@
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
+# blockjob.c
+block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
+block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
+block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
+
# block/block-backend.c
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
+blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
+blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p"
# block/io.c
bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
@@ -46,6 +53,8 @@ qmp_block_job_cancel(void *job) "job %p"
qmp_block_job_pause(void *job) "job %p"
qmp_block_job_resume(void *job) "job %p"
qmp_block_job_complete(void *job) "job %p"
+qmp_block_job_finalize(void *job) "job %p"
+qmp_block_job_dismiss(void *job) "job %p"
qmp_block_stream(void *bs, void *job) "bs %p job %p"
# block/file-win32.c
diff --git a/block/vdi.c b/block/vdi.c
index 2b5ddd0666..d939b034c4 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -51,6 +51,9 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
#include "qemu/module.h"
@@ -140,6 +143,8 @@
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
(uint64_t)DEFAULT_CLUSTER_SIZE)
+static QemuOptsList vdi_create_opts;
+
typedef struct {
char text[0x40];
uint32_t signature;
@@ -716,37 +721,47 @@ nonallocating_write:
return ret;
}
-static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
+ size_t block_size, Error **errp)
{
+ BlockdevCreateOptionsVdi *vdi_opts;
int ret = 0;
uint64_t bytes = 0;
uint32_t blocks;
- size_t block_size = DEFAULT_CLUSTER_SIZE;
uint32_t image_type = VDI_TYPE_DYNAMIC;
VdiHeader header;
size_t i;
size_t bmap_size;
int64_t offset = 0;
- Error *local_err = NULL;
+ BlockDriverState *bs_file = NULL;
BlockBackend *blk = NULL;
uint32_t *bmap = NULL;
+ assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
+ vdi_opts = &create_options->u.vdi;
+
logout("\n");
- /* Read out options. */
- bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
-#if defined(CONFIG_VDI_BLOCK_SIZE)
- /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
- block_size = qemu_opt_get_size_del(opts,
- BLOCK_OPT_CLUSTER_SIZE,
- DEFAULT_CLUSTER_SIZE);
-#endif
-#if defined(CONFIG_VDI_STATIC_IMAGE)
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
+ /* Validate options and set default values */
+ bytes = vdi_opts->size;
+ if (vdi_opts->q_static) {
image_type = VDI_TYPE_STATIC;
}
+#ifndef CONFIG_VDI_STATIC_IMAGE
+ if (image_type == VDI_TYPE_STATIC) {
+ ret = -ENOTSUP;
+ error_setg(errp, "Statically allocated images cannot be created in "
+ "this build");
+ goto exit;
+ }
+#endif
+#ifndef CONFIG_VDI_BLOCK_SIZE
+ if (block_size != DEFAULT_CLUSTER_SIZE) {
+ ret = -ENOTSUP;
+ error_setg(errp,
+ "A non-default cluster size is not supported in this build");
+ goto exit;
+ }
#endif
if (bytes > VDI_DISK_SIZE_MAX) {
@@ -757,18 +772,16 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
goto exit;
}
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
+ /* Create BlockBackend to write to the image */
+ bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
+ if (!bs_file) {
+ ret = -EIO;
goto exit;
}
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs_file, errp);
+ if (ret < 0) {
goto exit;
}
@@ -805,7 +818,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
vdi_header_to_le(&header);
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
if (ret < 0) {
- error_setg(errp, "Error writing header to %s", filename);
+ error_setg(errp, "Error writing header");
goto exit;
}
offset += sizeof(header);
@@ -826,7 +839,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
}
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
if (ret < 0) {
- error_setg(errp, "Error writing bmap to %s", filename);
+ error_setg(errp, "Error writing bmap");
goto exit;
}
offset += bmap_size;
@@ -836,17 +849,96 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
ret = blk_truncate(blk, offset + blocks * block_size,
PREALLOC_MODE_OFF, errp);
if (ret < 0) {
- error_prepend(errp, "Failed to statically allocate %s", filename);
+ error_prepend(errp, "Failed to statically allocate file");
goto exit;
}
}
exit:
blk_unref(blk);
+ bdrv_unref(bs_file);
g_free(bmap);
return ret;
}
+static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options,
+ Error **errp)
+{
+ return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
+}
+
+static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
+ Error **errp)
+{
+ QDict *qdict = NULL;
+ BlockdevCreateOptions *create_options = NULL;
+ BlockDriverState *bs_file = NULL;
+ uint64_t block_size = DEFAULT_CLUSTER_SIZE;
+ Visitor *v;
+ Error *local_err = NULL;
+ int ret;
+
+ /* Parse options and convert legacy syntax.
+ *
+ * Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
+ * cluster-size is not part of the QAPI schema; therefore we have
+ * to parse it before creating the QAPI object. */
+#if defined(CONFIG_VDI_BLOCK_SIZE)
+ block_size = qemu_opt_get_size_del(opts,
+ BLOCK_OPT_CLUSTER_SIZE,
+ DEFAULT_CLUSTER_SIZE);
+ if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX ||
+ !is_power_of_2(block_size))
+ {
+ error_setg(errp, "Invalid cluster size");
+ ret = -EINVAL;
+ goto done;
+ }
+#endif
+
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, errp);
+ if (ret < 0) {
+ goto done;
+ }
+
+ bs_file = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (!bs_file) {
+ ret = -EIO;
+ goto done;
+ }
+
+ qdict_put_str(qdict, "driver", "vdi");
+ qdict_put_str(qdict, "file", bs_file->node_name);
+
+ /* Get the QAPI object */
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* Silently round up size */
+ assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
+ create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
+ BDRV_SECTOR_SIZE);
+
+ /* Create the vdi image (format layer) */
+ ret = vdi_co_do_create(create_options, block_size, errp);
+done:
+ QDECREF(qdict);
+ qapi_free_BlockdevCreateOptions(create_options);
+ bdrv_unref(bs_file);
+ return ret;
+}
+
static void vdi_close(BlockDriverState *bs)
{
BDRVVdiState *s = bs->opaque;
@@ -895,6 +987,7 @@ static BlockDriver bdrv_vdi = {
.bdrv_close = vdi_close,
.bdrv_reopen_prepare = vdi_reopen_prepare,
.bdrv_child_perm = bdrv_format_default_perms,
+ .bdrv_co_create = vdi_co_create,
.bdrv_co_create_opts = vdi_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_block_status = vdi_co_block_status,
diff --git a/block/vhdx.c b/block/vhdx.c
index d82350d07c..d2c54b7891 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -26,6 +26,9 @@
#include "block/vhdx.h"
#include "migration/blocker.h"
#include "qemu/uuid.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
/* Options for VHDX creation */
@@ -39,6 +42,8 @@ typedef enum VHDXImageType {
VHDX_TYPE_DIFFERENCING, /* Currently unsupported */
} VHDXImageType;
+static QemuOptsList vhdx_create_opts;
+
/* Several metadata and region table data entries are identified by
* guids in a MS-specific GUID format. */
@@ -1792,59 +1797,71 @@ exit:
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
* 1MB
*/
-static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
+ Error **errp)
{
+ BlockdevCreateOptionsVhdx *vhdx_opts;
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
+
int ret = 0;
- uint64_t image_size = (uint64_t) 2 * GiB;
- uint32_t log_size = 1 * MiB;
- uint32_t block_size = 0;
+ uint64_t image_size;
+ uint32_t log_size;
+ uint32_t block_size;
uint64_t signature;
uint64_t metadata_offset;
bool use_zero_blocks = false;
gunichar2 *creator = NULL;
glong creator_items;
- BlockBackend *blk;
- char *type = NULL;
VHDXImageType image_type;
- Error *local_err = NULL;
- image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0);
- block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0);
- type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
- use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true);
+ assert(opts->driver == BLOCKDEV_DRIVER_VHDX);
+ vhdx_opts = &opts->u.vhdx;
+ /* Validate options and set default values */
+ image_size = vhdx_opts->size;
if (image_size > VHDX_MAX_IMAGE_SIZE) {
error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
- ret = -EINVAL;
- goto exit;
+ return -EINVAL;
}
- if (type == NULL) {
- type = g_strdup("dynamic");
+ if (!vhdx_opts->has_log_size) {
+ log_size = DEFAULT_LOG_SIZE;
+ } else {
+ log_size = vhdx_opts->log_size;
+ }
+ if (log_size < MiB || (log_size % MiB) != 0) {
+ error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB");
+ return -EINVAL;
}
- if (!strcmp(type, "dynamic")) {
+ if (!vhdx_opts->has_block_state_zero) {
+ use_zero_blocks = true;
+ } else {
+ use_zero_blocks = vhdx_opts->block_state_zero;
+ }
+
+ if (!vhdx_opts->has_subformat) {
+ vhdx_opts->subformat = BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC;
+ }
+
+ switch (vhdx_opts->subformat) {
+ case BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC:
image_type = VHDX_TYPE_DYNAMIC;
- } else if (!strcmp(type, "fixed")) {
+ break;
+ case BLOCKDEV_VHDX_SUBFORMAT_FIXED:
image_type = VHDX_TYPE_FIXED;
- } else if (!strcmp(type, "differencing")) {
- error_setg_errno(errp, ENOTSUP,
- "Differencing files not yet supported");
- ret = -ENOTSUP;
- goto exit;
- } else {
- error_setg(errp, "Invalid subformat '%s'", type);
- ret = -EINVAL;
- goto exit;
+ break;
+ default:
+ g_assert_not_reached();
}
/* These are pretty arbitrary, and mainly designed to keep the BAT
* size reasonable to load into RAM */
- if (block_size == 0) {
+ if (vhdx_opts->has_block_size) {
+ block_size = vhdx_opts->block_size;
+ } else {
if (image_size > 32 * TiB) {
block_size = 64 * MiB;
} else if (image_size > (uint64_t) 100 * GiB) {
@@ -1856,30 +1873,27 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts
}
}
-
- /* make the log size close to what was specified, but must be
- * min 1MB, and multiple of 1MB */
- log_size = ROUND_UP(log_size, MiB);
-
- block_size = ROUND_UP(block_size, MiB);
- block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
- block_size;
-
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- goto exit;
+ if (block_size < MiB || (block_size % MiB) != 0) {
+ error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB");
+ return -EINVAL;
+ }
+ if (block_size > VHDX_BLOCK_SIZE_MAX) {
+ error_setg_errno(errp, EINVAL, "Block size must not exceed %d",
+ VHDX_BLOCK_SIZE_MAX);
+ return -EINVAL;
}
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
- goto exit;
+ /* Create BlockBackend to write to the image */
+ bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp);
+ if (bs == NULL) {
+ return -EIO;
}
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto delete_and_exit;
+ }
blk_set_allow_write_beyond_eof(blk, true);
/* Create (A) */
@@ -1931,12 +1945,109 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts
delete_and_exit:
blk_unref(blk);
-exit:
- g_free(type);
+ bdrv_unref(bs);
g_free(creator);
return ret;
}
+static int coroutine_fn vhdx_co_create_opts(const char *filename,
+ QemuOpts *opts,
+ Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ BlockDriverState *bs = NULL;
+ Error *local_err = NULL;
+ int ret;
+
+ static const QDictRenames opt_renames[] = {
+ { VHDX_BLOCK_OPT_LOG_SIZE, "log-size" },
+ { VHDX_BLOCK_OPT_BLOCK_SIZE, "block-size" },
+ { VHDX_BLOCK_OPT_ZERO, "block-state-zero" },
+ { NULL, NULL },
+ };
+
+ /* Parse options and convert legacy syntax */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vhdx_create_opts, true);
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qdict_put_str(qdict, "driver", "vhdx");
+ qdict_put_str(qdict, "file", bs->node_name);
+
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to(QDict, qobj);
+ if (qdict == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Silently round up sizes:
+ * The image size is rounded to 512 bytes. Make the block and log size
+ * close to what was specified, but must be at least 1MB, and a multiple of
+ * 1 MB. Also respect VHDX_BLOCK_SIZE_MAX for block sizes. block_size = 0
+ * means auto, which is represented by a missing key in QAPI. */
+ assert(create_options->driver == BLOCKDEV_DRIVER_VHDX);
+ create_options->u.vhdx.size =
+ ROUND_UP(create_options->u.vhdx.size, BDRV_SECTOR_SIZE);
+
+ if (create_options->u.vhdx.has_log_size) {
+ create_options->u.vhdx.log_size =
+ ROUND_UP(create_options->u.vhdx.log_size, MiB);
+ }
+ if (create_options->u.vhdx.has_block_size) {
+ create_options->u.vhdx.block_size =
+ ROUND_UP(create_options->u.vhdx.block_size, MiB);
+
+ if (create_options->u.vhdx.block_size == 0) {
+ create_options->u.vhdx.has_block_size = false;
+ }
+ if (create_options->u.vhdx.block_size > VHDX_BLOCK_SIZE_MAX) {
+ create_options->u.vhdx.block_size = VHDX_BLOCK_SIZE_MAX;
+ }
+ }
+
+ /* Create the vhdx image (format layer) */
+ ret = vhdx_co_create(create_options, errp);
+
+fail:
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
+ return ret;
+}
+
/* If opened r/w, the VHDX driver will automatically replay the log,
* if one is present, inside the vhdx_open() call.
*
@@ -2005,6 +2116,7 @@ static BlockDriver bdrv_vhdx = {
.bdrv_child_perm = bdrv_format_default_perms,
.bdrv_co_readv = vhdx_co_readv,
.bdrv_co_writev = vhdx_co_writev,
+ .bdrv_co_create = vhdx_co_create,
.bdrv_co_create_opts = vhdx_co_create_opts,
.bdrv_get_info = vhdx_get_info,
.bdrv_co_check = vhdx_co_check,
diff --git a/block/vpc.c b/block/vpc.c
index b2e2b9ebd4..44f99a4d1b 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -32,6 +32,9 @@
#include "migration/blocker.h"
#include "qemu/bswap.h"
#include "qemu/uuid.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qapi-visit-block-core.h"
/**************************************************************/
@@ -166,6 +169,8 @@ static QemuOptsList vpc_runtime_opts = {
}
};
+static QemuOptsList vpc_create_opts;
+
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
{
uint32_t res = 0;
@@ -897,60 +902,19 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
return ret;
}
-static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
- Error **errp)
+static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
+ uint16_t *out_cyls,
+ uint8_t *out_heads,
+ uint8_t *out_secs_per_cyl,
+ int64_t *out_total_sectors,
+ Error **errp)
{
- uint8_t buf[1024];
- VHDFooter *footer = (VHDFooter *) buf;
- char *disk_type_param;
- int i;
+ int64_t total_size = vpc_opts->size;
uint16_t cyls = 0;
uint8_t heads = 0;
uint8_t secs_per_cyl = 0;
int64_t total_sectors;
- int64_t total_size;
- int disk_type;
- int ret = -EIO;
- bool force_size;
- Error *local_err = NULL;
- BlockBackend *blk = NULL;
-
- /* Read out options */
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
- BDRV_SECTOR_SIZE);
- disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
- if (disk_type_param) {
- if (!strcmp(disk_type_param, "dynamic")) {
- disk_type = VHD_DYNAMIC;
- } else if (!strcmp(disk_type_param, "fixed")) {
- disk_type = VHD_FIXED;
- } else {
- error_setg(errp, "Invalid disk type, %s", disk_type_param);
- ret = -EINVAL;
- goto out;
- }
- } else {
- disk_type = VHD_DYNAMIC;
- }
-
- force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
-
- ret = bdrv_create_file(filename, opts, &local_err);
- if (ret < 0) {
- error_propagate(errp, local_err);
- goto out;
- }
-
- blk = blk_new_open(filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
- &local_err);
- if (blk == NULL) {
- error_propagate(errp, local_err);
- ret = -EIO;
- goto out;
- }
-
- blk_set_allow_write_beyond_eof(blk, true);
+ int i;
/*
* Calculate matching total_size and geometry. Increase the number of
@@ -961,7 +925,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
* the image size from the VHD footer to calculate total_sectors.
*/
- if (force_size) {
+ if (vpc_opts->force_size) {
/* This will force the use of total_size for sector count, below */
cyls = VHD_CHS_MAX_C;
heads = VHD_CHS_MAX_H;
@@ -978,19 +942,95 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
/* Allow a maximum disk size of 2040 GiB */
if (total_sectors > VHD_MAX_SECTORS) {
error_setg(errp, "Disk size is too large, max size is 2040 GiB");
- ret = -EFBIG;
- goto out;
+ return -EFBIG;
}
} else {
- total_sectors = (int64_t)cyls * heads * secs_per_cyl;
- total_size = total_sectors * BDRV_SECTOR_SIZE;
+ total_sectors = (int64_t) cyls * heads * secs_per_cyl;
+ }
+
+ *out_total_sectors = total_sectors;
+ if (out_cyls) {
+ *out_cyls = cyls;
+ *out_heads = heads;
+ *out_secs_per_cyl = secs_per_cyl;
+ }
+
+ return 0;
+}
+
+static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
+ Error **errp)
+{
+ BlockdevCreateOptionsVpc *vpc_opts;
+ BlockBackend *blk = NULL;
+ BlockDriverState *bs = NULL;
+
+ uint8_t buf[1024];
+ VHDFooter *footer = (VHDFooter *) buf;
+ uint16_t cyls = 0;
+ uint8_t heads = 0;
+ uint8_t secs_per_cyl = 0;
+ int64_t total_sectors;
+ int64_t total_size;
+ int disk_type;
+ int ret = -EIO;
+
+ assert(opts->driver == BLOCKDEV_DRIVER_VPC);
+ vpc_opts = &opts->u.vpc;
+
+ /* Validate options and set default values */
+ total_size = vpc_opts->size;
+
+ if (!vpc_opts->has_subformat) {
+ vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC;
+ }
+ switch (vpc_opts->subformat) {
+ case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC:
+ disk_type = VHD_DYNAMIC;
+ break;
+ case BLOCKDEV_VPC_SUBFORMAT_FIXED:
+ disk_type = VHD_FIXED;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Create BlockBackend to write to the image */
+ bs = bdrv_open_blockdev_ref(vpc_opts->file, errp);
+ if (bs == NULL) {
+ return -EIO;
+ }
+
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
+ ret = blk_insert_bs(blk, bs, errp);
+ if (ret < 0) {
+ goto out;
+ }
+ blk_set_allow_write_beyond_eof(blk, true);
+
+ /* Get geometry and check that it matches the image size*/
+ ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
+ &total_sectors, errp);
+ if (ret < 0) {
+ goto out;
+ }
+
+ if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
+ error_setg(errp, "The requested image size cannot be represented in "
+ "CHS geometry");
+ error_append_hint(errp, "Try size=%llu or force-size=on (the "
+ "latter makes the image incompatible with "
+ "Virtual PC)",
+ total_sectors * BDRV_SECTOR_SIZE);
+ ret = -EINVAL;
+ goto out;
}
/* Prepare the Hard Disk Footer */
memset(buf, 0, 1024);
memcpy(footer->creator, "conectix", 8);
- if (force_size) {
+ if (vpc_opts->force_size) {
memcpy(footer->creator_app, "qem2", 4);
} else {
memcpy(footer->creator_app, "qemu", 4);
@@ -1032,10 +1072,98 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
out:
blk_unref(blk);
- g_free(disk_type_param);
+ bdrv_unref(bs);
+ return ret;
+}
+
+static int coroutine_fn vpc_co_create_opts(const char *filename,
+ QemuOpts *opts, Error **errp)
+{
+ BlockdevCreateOptions *create_options = NULL;
+ QDict *qdict = NULL;
+ QObject *qobj;
+ Visitor *v;
+ BlockDriverState *bs = NULL;
+ Error *local_err = NULL;
+ int ret;
+
+ static const QDictRenames opt_renames[] = {
+ { VPC_OPT_FORCE_SIZE, "force-size" },
+ { NULL, NULL },
+ };
+
+ /* Parse options and convert legacy syntax */
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true);
+
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Create and open the file (protocol layer) */
+ ret = bdrv_create_file(filename, opts, &local_err);
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+
+ bs = bdrv_open(filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
+ if (bs == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* Now get the QAPI type BlockdevCreateOptions */
+ qdict_put_str(qdict, "driver", "vpc");
+ qdict_put_str(qdict, "file", bs->node_name);
+
+ qobj = qdict_crumple(qdict, errp);
+ QDECREF(qdict);
+ qdict = qobject_to(QDict, qobj);
+ if (qdict == NULL) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
+ visit_free(v);
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* Silently round up size */
+ assert(create_options->driver == BLOCKDEV_DRIVER_VPC);
+ create_options->u.vpc.size =
+ ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
+
+ if (!create_options->u.vpc.force_size) {
+ int64_t total_sectors;
+ ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
+ NULL, &total_sectors, errp);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
+ }
+
+
+ /* Create the vpc image (format layer) */
+ ret = vpc_co_create(create_options, errp);
+
+fail:
+ QDECREF(qdict);
+ bdrv_unref(bs);
+ qapi_free_BlockdevCreateOptions(create_options);
return ret;
}
+
static int vpc_has_zero_init(BlockDriverState *bs)
{
BDRVVPCState *s = bs->opaque;
@@ -1096,6 +1224,7 @@ static BlockDriver bdrv_vpc = {
.bdrv_close = vpc_close,
.bdrv_reopen_prepare = vpc_reopen_prepare,
.bdrv_child_perm = bdrv_format_default_perms,
+ .bdrv_co_create = vpc_co_create,
.bdrv_co_create_opts = vpc_co_create_opts,
.bdrv_co_preadv = vpc_co_preadv,
diff --git a/block/vvfat.c b/block/vvfat.c
index 4a17a49e12..1569783b0f 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3129,7 +3129,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
int parent_flags, QDict *parent_options)
{
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
- *child_flags = BDRV_O_NO_FLUSH;
+ qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
}
static const BdrvChildRole child_vvfat_qcow = {
diff --git a/blockdev.c b/blockdev.c
index 1fbfd3a2c4..c31bf3d98d 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -150,7 +150,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
aio_context_acquire(aio_context);
if (bs->job) {
- block_job_cancel(bs->job);
+ block_job_cancel(bs->job, false);
}
aio_context_release(aio_context);
@@ -334,7 +334,8 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
case QTYPE_QSTRING: {
unsigned long long length;
- const char *str = qstring_get_str(qobject_to_qstring(entry->value));
+ const char *str = qstring_get_str(qobject_to(QString,
+ entry->value));
if (parse_uint_full(str, &length, 10) == 0 &&
length > 0 && length <= UINT_MAX) {
block_acct_add_interval(stats, (unsigned) length);
@@ -346,7 +347,7 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
}
case QTYPE_QNUM: {
- int64_t length = qnum_get_int(qobject_to_qnum(entry->value));
+ int64_t length = qnum_get_int(qobject_to(QNum, entry->value));
if (length > 0 && length <= UINT_MAX) {
block_acct_add_interval(stats, (unsigned) length);
@@ -2118,6 +2119,9 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
error_setg(errp, "Cannot modify a frozen bitmap");
return;
+ } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
+ error_setg(errp, "Cannot modify a locked bitmap");
+ return;
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
error_setg(errp, "Cannot clear a disabled bitmap");
return;
@@ -2862,6 +2866,11 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
"Bitmap '%s' is currently frozen and cannot be removed",
name);
return;
+ } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently locked and cannot be removed",
+ name);
+ return;
}
if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
@@ -2896,6 +2905,11 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
"Bitmap '%s' is currently frozen and cannot be modified",
name);
return;
+ } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently locked and cannot be modified",
+ name);
+ return;
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently disabled and cannot be cleared",
@@ -3261,7 +3275,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
AioContext *aio_context;
QDict *options = NULL;
Error *local_err = NULL;
- int flags;
+ int flags, job_flags = BLOCK_JOB_DEFAULT;
int64_t size;
bool set_backing_hd = false;
@@ -3280,6 +3294,12 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
if (!backup->has_job_id) {
backup->job_id = NULL;
}
+ if (!backup->has_auto_finalize) {
+ backup->auto_finalize = true;
+ }
+ if (!backup->has_auto_dismiss) {
+ backup->auto_dismiss = true;
+ }
if (!backup->has_compress) {
backup->compress = false;
}
@@ -3370,12 +3390,24 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
bdrv_unref(target_bs);
goto out;
}
+ if (bdrv_dirty_bitmap_qmp_locked(bmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently locked and cannot be used for "
+ "backup", backup->bitmap);
+ goto out;
+ }
+ }
+ if (!backup->auto_finalize) {
+ job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
+ }
+ if (!backup->auto_dismiss) {
+ job_flags |= BLOCK_JOB_MANUAL_DISMISS;
}
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, bmap, backup->compress,
backup->on_source_error, backup->on_target_error,
- BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
+ job_flags, NULL, NULL, txn, &local_err);
bdrv_unref(target_bs);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3410,6 +3442,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
Error *local_err = NULL;
AioContext *aio_context;
BlockJob *job = NULL;
+ int job_flags = BLOCK_JOB_DEFAULT;
if (!backup->has_speed) {
backup->speed = 0;
@@ -3423,6 +3456,12 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
if (!backup->has_job_id) {
backup->job_id = NULL;
}
+ if (!backup->has_auto_finalize) {
+ backup->auto_finalize = true;
+ }
+ if (!backup->has_auto_dismiss) {
+ backup->auto_dismiss = true;
+ }
if (!backup->has_compress) {
backup->compress = false;
}
@@ -3451,10 +3490,16 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
goto out;
}
}
+ if (!backup->auto_finalize) {
+ job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
+ }
+ if (!backup->auto_dismiss) {
+ job_flags |= BLOCK_JOB_MANUAL_DISMISS;
+ }
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, NULL, backup->compress,
backup->on_source_error, backup->on_target_error,
- BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
+ job_flags, NULL, NULL, txn, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
@@ -3806,7 +3851,7 @@ void qmp_block_job_cancel(const char *device,
}
trace_qmp_block_job_cancel(job);
- block_job_cancel(job);
+ block_job_user_cancel(job, force, errp);
out:
aio_context_release(aio_context);
}
@@ -3816,12 +3861,12 @@ void qmp_block_job_pause(const char *device, Error **errp)
AioContext *aio_context;
BlockJob *job = find_block_job(device, &aio_context, errp);
- if (!job || block_job_user_paused(job)) {
+ if (!job) {
return;
}
trace_qmp_block_job_pause(job);
- block_job_user_pause(job);
+ block_job_user_pause(job, errp);
aio_context_release(aio_context);
}
@@ -3830,12 +3875,12 @@ void qmp_block_job_resume(const char *device, Error **errp)
AioContext *aio_context;
BlockJob *job = find_block_job(device, &aio_context, errp);
- if (!job || !block_job_user_paused(job)) {
+ if (!job) {
return;
}
trace_qmp_block_job_resume(job);
- block_job_user_resume(job);
+ block_job_user_resume(job, errp);
aio_context_release(aio_context);
}
@@ -3853,6 +3898,34 @@ void qmp_block_job_complete(const char *device, Error **errp)
aio_context_release(aio_context);
}
+void qmp_block_job_finalize(const char *id, Error **errp)
+{
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(id, &aio_context, errp);
+
+ if (!job) {
+ return;
+ }
+
+ trace_qmp_block_job_finalize(job);
+ block_job_finalize(job, errp);
+ aio_context_release(aio_context);
+}
+
+void qmp_block_job_dismiss(const char *id, Error **errp)
+{
+ AioContext *aio_context;
+ BlockJob *job = find_block_job(id, &aio_context, errp);
+
+ if (!job) {
+ return;
+ }
+
+ trace_qmp_block_job_dismiss(job);
+ block_job_dismiss(&job, errp);
+ aio_context_release(aio_context);
+}
+
void qmp_change_backing_file(const char *device,
const char *image_node_name,
const char *backing_file,
@@ -3972,7 +4045,6 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
QDict *qdict;
- const QDictEntry *ent;
Error *local_err = NULL;
visit_type_BlockdevOptions(v, NULL, &options, &local_err);
@@ -3982,23 +4054,10 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
}
visit_complete(v, &obj);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
- /*
- * Rewrite "backing": null to "backing": ""
- * TODO Rewrite "" to null instead, and perhaps not even here
- */
- for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
- char *dot = strrchr(ent->key, '.');
-
- if (!strcmp(dot ? dot + 1 : ent->key, "backing")
- && qobject_type(ent->value) == QTYPE_QNULL) {
- qdict_put(qdict, ent->key, qstring_new());
- }
- }
-
if (!qdict_get_try_str(qdict, "node-name")) {
error_setg(errp, "'node-name' must be specified for the root node");
goto fail;
@@ -4180,6 +4239,49 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
aio_context_release(old_context);
}
+void qmp_x_block_latency_histogram_set(
+ const char *device,
+ bool has_boundaries, uint64List *boundaries,
+ bool has_boundaries_read, uint64List *boundaries_read,
+ bool has_boundaries_write, uint64List *boundaries_write,
+ bool has_boundaries_flush, uint64List *boundaries_flush,
+ Error **errp)
+{
+ BlockBackend *blk = blk_by_name(device);
+ BlockAcctStats *stats;
+
+ if (!blk) {
+ error_setg(errp, "Device '%s' not found", device);
+ return;
+ }
+ stats = blk_get_stats(blk);
+
+ if (!has_boundaries && !has_boundaries_read && !has_boundaries_write &&
+ !has_boundaries_flush)
+ {
+ block_latency_histograms_clear(stats);
+ return;
+ }
+
+ if (has_boundaries || has_boundaries_read) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_READ,
+ has_boundaries_read ? boundaries_read : boundaries);
+ }
+
+ if (has_boundaries || has_boundaries_write) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_WRITE,
+ has_boundaries_write ? boundaries_write : boundaries);
+ }
+
+ if (has_boundaries || has_boundaries_flush) {
+ block_latency_histogram_set(
+ stats, BLOCK_ACCT_FLUSH,
+ has_boundaries_flush ? boundaries_flush : boundaries);
+ }
+}
+
QemuOptsList qemu_common_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
diff --git a/blockjob.c b/blockjob.c
index 801d29d849..ef3ed69ff1 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -28,6 +28,7 @@
#include "block/block.h"
#include "block/blockjob_int.h"
#include "block/block_int.h"
+#include "block/trace.h"
#include "sysemu/block-backend.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block-core.h"
@@ -41,6 +42,64 @@
* block_job_enter. */
static QemuMutex block_job_mutex;
+/* BlockJob State Transition Table */
+bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
+ /* U, C, R, P, Y, S, W, D, X, E, N */
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
+ /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
+ /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
+ /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
+ /* U, C, R, P, Y, S, W, D, X, E, N */
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
+ [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
+ [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
+};
+
+static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
+{
+ BlockJobStatus s0 = job->status;
+ assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
+ trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
+ "allowed" : "disallowed",
+ qapi_enum_lookup(&BlockJobStatus_lookup,
+ s0),
+ qapi_enum_lookup(&BlockJobStatus_lookup,
+ s1));
+ assert(BlockJobSTT[s0][s1]);
+ job->status = s1;
+}
+
+static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
+{
+ assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
+ trace_block_job_apply_verb(job, qapi_enum_lookup(&BlockJobStatus_lookup,
+ job->status),
+ qapi_enum_lookup(&BlockJobVerb_lookup, bv),
+ BlockJobVerbTable[bv][job->status] ?
+ "allowed" : "prohibited");
+ if (BlockJobVerbTable[bv][job->status]) {
+ return 0;
+ }
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
+ job->id, qapi_enum_lookup(&BlockJobStatus_lookup, job->status),
+ qapi_enum_lookup(&BlockJobVerb_lookup, bv));
+ return -EPERM;
+}
+
static void block_job_lock(void)
{
qemu_mutex_lock(&block_job_mutex);
@@ -58,6 +117,7 @@ static void __attribute__((__constructor__)) block_job_init(void)
static void block_job_event_cancelled(BlockJob *job);
static void block_job_event_completed(BlockJob *job, const char *msg);
+static int block_job_event_pending(BlockJob *job);
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
/* Transactional group of block jobs */
@@ -171,6 +231,7 @@ static void block_job_detach_aio_context(void *opaque);
void block_job_unref(BlockJob *job)
{
if (--job->refcnt == 0) {
+ assert(job->status == BLOCK_JOB_STATUS_NULL);
BlockDriverState *bs = blk_bs(job->blk);
QLIST_REMOVE(job, job_list);
bs->job = NULL;
@@ -320,25 +381,88 @@ void block_job_start(BlockJob *job)
job->pause_count--;
job->busy = true;
job->paused = false;
+ block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
}
-static void block_job_completed_single(BlockJob *job)
+static void block_job_decommission(BlockJob *job)
{
- assert(job->completed);
+ assert(job);
+ job->completed = true;
+ job->busy = false;
+ job->paused = false;
+ job->deferred_to_main_loop = true;
+ block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
+ block_job_unref(job);
+}
- if (!job->ret) {
- if (job->driver->commit) {
- job->driver->commit(job);
- }
- } else {
- if (job->driver->abort) {
- job->driver->abort(job);
- }
+static void block_job_do_dismiss(BlockJob *job)
+{
+ block_job_decommission(job);
+}
+
+static void block_job_conclude(BlockJob *job)
+{
+ block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
+ if (job->auto_dismiss || !block_job_started(job)) {
+ block_job_do_dismiss(job);
+ }
+}
+
+static void block_job_update_rc(BlockJob *job)
+{
+ if (!job->ret && block_job_is_cancelled(job)) {
+ job->ret = -ECANCELED;
+ }
+ if (job->ret) {
+ block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
+ }
+}
+
+static int block_job_prepare(BlockJob *job)
+{
+ if (job->ret == 0 && job->driver->prepare) {
+ job->ret = job->driver->prepare(job);
+ }
+ return job->ret;
+}
+
+static void block_job_commit(BlockJob *job)
+{
+ assert(!job->ret);
+ if (job->driver->commit) {
+ job->driver->commit(job);
}
+}
+
+static void block_job_abort(BlockJob *job)
+{
+ assert(job->ret);
+ if (job->driver->abort) {
+ job->driver->abort(job);
+ }
+}
+
+static void block_job_clean(BlockJob *job)
+{
if (job->driver->clean) {
job->driver->clean(job);
}
+}
+
+static int block_job_finalize_single(BlockJob *job)
+{
+ assert(job->completed);
+
+ /* Ensure abort is called for late-transactional failures */
+ block_job_update_rc(job);
+
+ if (!job->ret) {
+ block_job_commit(job);
+ } else {
+ block_job_abort(job);
+ }
+ block_job_clean(job);
if (job->cb) {
job->cb(job->opaque, job->ret);
@@ -357,14 +481,13 @@ static void block_job_completed_single(BlockJob *job)
}
}
- if (job->txn) {
- QLIST_REMOVE(job, txn_list);
- block_job_txn_unref(job->txn);
- }
- block_job_unref(job);
+ QLIST_REMOVE(job, txn_list);
+ block_job_txn_unref(job->txn);
+ block_job_conclude(job);
+ return 0;
}
-static void block_job_cancel_async(BlockJob *job)
+static void block_job_cancel_async(BlockJob *job, bool force)
{
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
block_job_iostatus_reset(job);
@@ -375,6 +498,30 @@ static void block_job_cancel_async(BlockJob *job)
job->pause_count--;
}
job->cancelled = true;
+ /* To prevent 'force == false' overriding a previous 'force == true' */
+ job->force |= force;
+}
+
+static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
+{
+ AioContext *ctx;
+ BlockJob *job, *next;
+ int rc = 0;
+
+ QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
+ if (lock) {
+ ctx = blk_get_aio_context(job->blk);
+ aio_context_acquire(ctx);
+ }
+ rc = fn(job);
+ if (lock) {
+ aio_context_release(ctx);
+ }
+ if (rc) {
+ break;
+ }
+ }
+ return rc;
}
static int block_job_finish_sync(BlockJob *job,
@@ -436,7 +583,7 @@ static void block_job_completed_txn_abort(BlockJob *job)
* on the caller, so leave it. */
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
if (other_job != job) {
- block_job_cancel_async(other_job);
+ block_job_cancel_async(other_job, false);
}
}
while (!QLIST_EMPTY(&txn->jobs)) {
@@ -446,18 +593,39 @@ static void block_job_completed_txn_abort(BlockJob *job)
assert(other_job->cancelled);
block_job_finish_sync(other_job, NULL, NULL);
}
- block_job_completed_single(other_job);
+ block_job_finalize_single(other_job);
aio_context_release(ctx);
}
block_job_txn_unref(txn);
}
+static int block_job_needs_finalize(BlockJob *job)
+{
+ return !job->auto_finalize;
+}
+
+static void block_job_do_finalize(BlockJob *job)
+{
+ int rc;
+ assert(job && job->txn);
+
+ /* prepare the transaction to complete */
+ rc = block_job_txn_apply(job->txn, block_job_prepare, true);
+ if (rc) {
+ block_job_completed_txn_abort(job);
+ } else {
+ block_job_txn_apply(job->txn, block_job_finalize_single, true);
+ }
+}
+
static void block_job_completed_txn_success(BlockJob *job)
{
- AioContext *ctx;
BlockJobTxn *txn = job->txn;
- BlockJob *other_job, *next;
+ BlockJob *other_job;
+
+ block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
+
/*
* Successful completion, see if there are other running jobs in this
* txn.
@@ -466,14 +634,14 @@ static void block_job_completed_txn_success(BlockJob *job)
if (!other_job->completed) {
return;
}
- }
- /* We are the last completed job, commit the transaction. */
- QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
- ctx = blk_get_aio_context(other_job->blk);
- aio_context_acquire(ctx);
assert(other_job->ret == 0);
- block_job_completed_single(other_job);
- aio_context_release(ctx);
+ }
+
+ block_job_txn_apply(txn, block_job_event_pending, false);
+
+ /* If no jobs need manual finalization, automatically do so */
+ if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
+ block_job_do_finalize(job);
}
}
@@ -492,6 +660,9 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
error_setg(errp, QERR_UNSUPPORTED);
return;
}
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
+ return;
+ }
job->driver->set_speed(job, speed, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -499,7 +670,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
}
job->speed = speed;
- if (speed <= old_speed) {
+ if (speed && speed <= old_speed) {
return;
}
@@ -511,8 +682,10 @@ void block_job_complete(BlockJob *job, Error **errp)
{
/* Should not be reachable via external interface for internal jobs */
assert(job->id);
- if (job->pause_count || job->cancelled ||
- !block_job_started(job) || !job->driver->complete) {
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
+ return;
+ }
+ if (job->pause_count || job->cancelled || !job->driver->complete) {
error_setg(errp, "The active block job '%s' cannot be completed",
job->id);
return;
@@ -521,8 +694,37 @@ void block_job_complete(BlockJob *job, Error **errp)
job->driver->complete(job, errp);
}
-void block_job_user_pause(BlockJob *job)
+void block_job_finalize(BlockJob *job, Error **errp)
+{
+ assert(job && job->id && job->txn);
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
+ return;
+ }
+ block_job_do_finalize(job);
+}
+
+void block_job_dismiss(BlockJob **jobptr, Error **errp)
+{
+ BlockJob *job = *jobptr;
+ /* similarly to _complete, this is QMP-interface only. */
+ assert(job->id);
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
+ return;
+ }
+
+ block_job_do_dismiss(job);
+ *jobptr = NULL;
+}
+
+void block_job_user_pause(BlockJob *job, Error **errp)
{
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
+ return;
+ }
+ if (job->user_paused) {
+ error_setg(errp, "Job is already paused");
+ return;
+ }
job->user_paused = true;
block_job_pause(job);
}
@@ -532,31 +734,51 @@ bool block_job_user_paused(BlockJob *job)
return job->user_paused;
}
-void block_job_user_resume(BlockJob *job)
+void block_job_user_resume(BlockJob *job, Error **errp)
{
- if (job && job->user_paused && job->pause_count > 0) {
- block_job_iostatus_reset(job);
- job->user_paused = false;
- block_job_resume(job);
+ assert(job);
+ if (!job->user_paused || job->pause_count <= 0) {
+ error_setg(errp, "Can't resume a job that was not paused");
+ return;
+ }
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
+ return;
}
+ block_job_iostatus_reset(job);
+ job->user_paused = false;
+ block_job_resume(job);
}
-void block_job_cancel(BlockJob *job)
+void block_job_cancel(BlockJob *job, bool force)
{
- if (block_job_started(job)) {
- block_job_cancel_async(job);
- block_job_enter(job);
- } else {
+ if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
+ block_job_do_dismiss(job);
+ return;
+ }
+ block_job_cancel_async(job, force);
+ if (!block_job_started(job)) {
block_job_completed(job, -ECANCELED);
+ } else if (job->deferred_to_main_loop) {
+ block_job_completed_txn_abort(job);
+ } else {
+ block_job_enter(job);
}
}
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
+{
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
+ return;
+ }
+ block_job_cancel(job, force);
+}
+
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
* used with block_job_finish_sync() without the need for (rather nasty)
* function pointer casts there. */
static void block_job_cancel_err(BlockJob *job, Error **errp)
{
- block_job_cancel(job);
+ block_job_cancel(job, false);
}
int block_job_cancel_sync(BlockJob *job)
@@ -600,6 +822,9 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
info->speed = job->speed;
info->io_status = job->iostatus;
info->ready = job->ready;
+ info->status = job->status;
+ info->auto_finalize = job->auto_finalize;
+ info->auto_dismiss = job->auto_dismiss;
return info;
}
@@ -641,13 +866,24 @@ static void block_job_event_completed(BlockJob *job, const char *msg)
&error_abort);
}
+static int block_job_event_pending(BlockJob *job)
+{
+ block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
+ if (!job->auto_finalize && !block_job_is_internal(job)) {
+ qapi_event_send_block_job_pending(job->driver->job_type,
+ job->id,
+ &error_abort);
+ }
+ return 0;
+}
+
/*
* API for block job drivers and the block layer. These functions are
* declared in blockjob_int.h.
*/
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
- BlockDriverState *bs, uint64_t perm,
+ BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
uint64_t shared_perm, int64_t speed, int flags,
BlockCompletionFunc *cb, void *opaque, Error **errp)
{
@@ -702,6 +938,9 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
job->paused = true;
job->pause_count = 1;
job->refcnt = 1;
+ job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
+ job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
+ block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
QEMU_CLOCK_REALTIME, SCALE_NS,
block_job_sleep_timer_cb, job);
@@ -724,11 +963,22 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
block_job_set_speed(job, speed, &local_err);
if (local_err) {
- block_job_unref(job);
+ block_job_early_fail(job);
error_propagate(errp, local_err);
return NULL;
}
}
+
+ /* Single jobs are modeled as single-job transactions for sake of
+ * consolidating the job management logic */
+ if (!txn) {
+ txn = block_job_txn_new();
+ block_job_txn_add_job(txn, job);
+ block_job_txn_unref(txn);
+ } else {
+ block_job_txn_add_job(txn, job);
+ }
+
return job;
}
@@ -747,18 +997,19 @@ void block_job_pause_all(void)
void block_job_early_fail(BlockJob *job)
{
- block_job_unref(job);
+ assert(job->status == BLOCK_JOB_STATUS_CREATED);
+ block_job_decommission(job);
}
void block_job_completed(BlockJob *job, int ret)
{
+ assert(job && job->txn && !job->completed);
assert(blk_bs(job->blk)->job == job);
- assert(!job->completed);
job->completed = true;
job->ret = ret;
- if (!job->txn) {
- block_job_completed_single(job);
- } else if (ret < 0 || block_job_is_cancelled(job)) {
+ block_job_update_rc(job);
+ trace_block_job_completed(job, ret, job->ret);
+ if (job->ret) {
block_job_completed_txn_abort(job);
} else {
block_job_completed_txn_success(job);
@@ -806,9 +1057,14 @@ void coroutine_fn block_job_pause_point(BlockJob *job)
}
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
+ BlockJobStatus status = job->status;
+ block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
+ BLOCK_JOB_STATUS_STANDBY : \
+ BLOCK_JOB_STATUS_PAUSED);
job->paused = true;
block_job_do_yield(job, -1);
job->paused = false;
+ block_job_state_transition(job, status);
}
if (job->driver->resume) {
@@ -914,6 +1170,7 @@ void block_job_iostatus_reset(BlockJob *job)
void block_job_event_ready(BlockJob *job)
{
+ block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
job->ready = true;
if (block_job_is_internal(job)) {
@@ -957,8 +1214,9 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
action, &error_abort);
}
if (action == BLOCK_ERROR_ACTION_STOP) {
+ block_job_pause(job);
/* make the pause user visible, which will be resumed from QMP. */
- block_job_user_pause(job);
+ job->user_paused = true;
block_job_iostatus_set_err(job, error);
}
return action;
diff --git a/bsd-user/main.c b/bsd-user/main.c
index efef5ff8c5..283dc6fd25 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -649,7 +649,7 @@ void cpu_loop(CPUSPARCState *env)
static void usage(void)
{
- printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
+ printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
"\n" QEMU_COPYRIGHT "\n"
"usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
"BSD CPU emulator (compiled for %s emulation)\n"
@@ -723,6 +723,7 @@ int main(int argc, char **argv)
{
const char *filename;
const char *cpu_model;
+ const char *cpu_type;
const char *log_file = NULL;
const char *log_mask = NULL;
struct target_pt_regs regs1, *regs = &regs1;
@@ -900,7 +901,8 @@ int main(int argc, char **argv)
tcg_exec_init(0);
/* NOTE: we need to init the CPU at this stage to get
qemu_host_page_size */
- cpu = cpu_init(cpu_model);
+ cpu_type = parse_cpu_model(cpu_model);
+ cpu = cpu_create(cpu_type);
env = cpu->env_ptr;
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
cpu_reset(cpu);
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index d48e78103a..1b925c8dec 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -27,6 +27,7 @@
#include "qemu/option.h"
#include "chardev/char.h"
#include "sysemu/block-backend.h"
+#include "sysemu/sysemu.h"
#include "chardev/char-mux.h"
/* MUX driver for serial I/O splitting */
@@ -230,14 +231,12 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
}
}
-bool muxes_realized;
-
void mux_chr_send_all_event(Chardev *chr, int event)
{
MuxChardev *d = MUX_CHARDEV(chr);
int i;
- if (!muxes_realized) {
+ if (!machine_init_done) {
return;
}
@@ -327,7 +326,7 @@ static void qemu_chr_open_mux(Chardev *chr,
/* only default to opened state if we've realized the initial
* set of muxes
*/
- *be_opened = muxes_realized;
+ *be_opened = machine_init_done;
qemu_chr_fe_init(&d->chr, drv, errp);
}
@@ -347,6 +346,31 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
mux->chardev = g_strdup(chardev);
}
+/**
+ * Called after processing of default and command-line-specified
+ * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
+ * to a mux chardev. This is done here to ensure that
+ * output/prompts/banners are only displayed for the FE that has
+ * focus when initial command-line processing/machine init is
+ * completed.
+ *
+ * After this point, any new FE attached to any new or existing
+ * mux will receive CHR_EVENT_OPENED notifications for the BE
+ * immediately.
+ */
+static int open_muxes(Chardev *chr)
+{
+ /* send OPENED to all already-attached FEs */
+ mux_chr_send_all_event(chr, CHR_EVENT_OPENED);
+ /*
+ * mark mux as OPENED so any new FEs will immediately receive
+ * OPENED event
+ */
+ qemu_chr_be_event(chr, CHR_EVENT_OPENED);
+
+ return 0;
+}
+
static void char_mux_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -357,6 +381,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
cc->chr_accept_input = mux_chr_accept_input;
cc->chr_add_watch = mux_chr_add_watch;
cc->chr_be_event = mux_chr_be_event;
+ cc->chr_machine_done = open_muxes;
}
static const TypeInfo char_mux_type_info = {
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index a220803c01..d057192ced 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -32,6 +32,7 @@
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-sockets.h"
+#include "sysemu/sysemu.h"
#include "chardev/char-io.h"
@@ -41,6 +42,11 @@
#define TCP_MAX_FDS 16
typedef struct {
+ char buf[21];
+ size_t buflen;
+} TCPChardevTelnetInit;
+
+typedef struct {
Chardev parent;
QIOChannel *ioc; /* Client I/O channel */
QIOChannelSocket *sioc; /* Client master channel */
@@ -60,6 +66,8 @@ typedef struct {
bool is_listen;
bool is_telnet;
bool is_tn3270;
+ GSource *telnet_source;
+ TCPChardevTelnetInit *telnet_init;
GSource *reconnect_timer;
int64_t reconnect_time;
@@ -70,6 +78,7 @@ typedef struct {
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
static gboolean socket_reconnect_timeout(gpointer opaque);
+static void tcp_chr_telnet_init(Chardev *chr);
static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
{
@@ -423,8 +432,8 @@ static void tcp_chr_disconnect(Chardev *chr)
tcp_chr_free_connection(chr);
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
- chr, NULL);
+ qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
+ chr, NULL, chr->gcontext);
}
update_disconnected_filename(s);
if (emit_close) {
@@ -450,7 +459,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
len = s->max_size;
}
size = tcp_chr_recv(chr, (void *)buf, len);
- if (size == 0 || size == -1) {
+ if (size == 0 || (size == -1 && errno != EAGAIN)) {
/* connection closed */
tcp_chr_disconnect(chr);
} else if (size > 0) {
@@ -556,10 +565,33 @@ static void tcp_chr_connect(void *opaque)
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
+static void tcp_chr_telnet_destroy(SocketChardev *s)
+{
+ if (s->telnet_source) {
+ g_source_destroy(s->telnet_source);
+ g_source_unref(s->telnet_source);
+ s->telnet_source = NULL;
+ }
+}
+
static void tcp_chr_update_read_handler(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
+ if (s->listener) {
+ /*
+ * It's possible that chardev context is changed in
+ * qemu_chr_be_update_read_handlers(). Reset it for QIO net
+ * listener if there is.
+ */
+ qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
+ chr, NULL, chr->gcontext);
+ }
+
+ if (s->telnet_source) {
+ tcp_chr_telnet_init(CHARDEV(s));
+ }
+
if (!s->connected) {
return;
}
@@ -573,32 +605,30 @@ static void tcp_chr_update_read_handler(Chardev *chr)
}
}
-typedef struct {
- Chardev *chr;
- char buf[21];
- size_t buflen;
-} TCPChardevTelnetInit;
-
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
GIOCondition cond G_GNUC_UNUSED,
gpointer user_data)
{
- TCPChardevTelnetInit *init = user_data;
+ SocketChardev *s = user_data;
+ Chardev *chr = CHARDEV(s);
+ TCPChardevTelnetInit *init = s->telnet_init;
ssize_t ret;
+ assert(init);
+
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0;
} else {
- tcp_chr_disconnect(init->chr);
+ tcp_chr_disconnect(chr);
goto end;
}
}
init->buflen -= ret;
if (init->buflen == 0) {
- tcp_chr_connect(init->chr);
+ tcp_chr_connect(chr);
goto end;
}
@@ -607,16 +637,30 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
return G_SOURCE_CONTINUE;
end:
- g_free(init);
+ g_free(s->telnet_init);
+ s->telnet_init = NULL;
+ g_source_unref(s->telnet_source);
+ s->telnet_source = NULL;
return G_SOURCE_REMOVE;
}
static void tcp_chr_telnet_init(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
- TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
+ TCPChardevTelnetInit *init;
size_t n = 0;
+ /* Destroy existing task */
+ tcp_chr_telnet_destroy(s);
+
+ if (s->telnet_init) {
+ /* We are possibly during a handshake already */
+ goto cont;
+ }
+
+ s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
+ init = s->telnet_init;
+
#define IACSET(x, a, b, c) \
do { \
x[n++] = a; \
@@ -624,7 +668,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \
} while (0)
- init->chr = chr;
if (!s->is_tn3270) {
init->buflen = 12;
/* Prep the telnet negotion to put telnet in binary,
@@ -647,10 +690,11 @@ static void tcp_chr_telnet_init(Chardev *chr)
#undef IACSET
- qio_channel_add_watch(
- s->ioc, G_IO_OUT,
- tcp_chr_telnet_init_io,
- init, NULL);
+cont:
+ s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
+ tcp_chr_telnet_init_io,
+ s, NULL,
+ chr->gcontext);
}
@@ -663,8 +707,7 @@ static void tcp_chr_tls_handshake(QIOTask *task,
if (qio_task_propagate_error(task, NULL)) {
tcp_chr_disconnect(chr);
} else {
- /* tn3270 does not support TLS yet */
- if (s->do_telnetopt && !s->is_tn3270) {
+ if (s->do_telnetopt) {
tcp_chr_telnet_init(chr);
} else {
tcp_chr_connect(chr);
@@ -680,6 +723,11 @@ static void tcp_chr_tls_init(Chardev *chr)
Error *err = NULL;
gchar *name;
+ if (!machine_init_done) {
+ /* This will be postponed to machine_done notifier */
+ return;
+ }
+
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
s->ioc, s->tls_creds,
@@ -708,7 +756,7 @@ static void tcp_chr_tls_init(Chardev *chr)
tcp_chr_tls_handshake,
chr,
NULL,
- NULL);
+ chr->gcontext);
}
@@ -744,7 +792,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
qio_channel_set_delay(s->ioc, false);
}
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
+ NULL, chr->gcontext);
}
if (s->tls_creds) {
@@ -824,8 +873,11 @@ static void char_socket_finalize(Object *obj)
tcp_chr_free_connection(chr);
tcp_chr_reconn_timer_cancel(s);
qapi_free_SocketAddress(s->addr);
+ tcp_chr_telnet_destroy(s);
+ g_free(s->telnet_init);
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
+ NULL, chr->gcontext);
object_unref(OBJECT(s->listener));
}
if (s->tls_creds) {
@@ -855,11 +907,22 @@ cleanup:
object_unref(OBJECT(sioc));
}
+static void tcp_chr_connect_async(Chardev *chr)
+{
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+ QIOChannelSocket *sioc;
+
+ sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
+ qio_channel_socket_connect_async(sioc, s->addr,
+ qemu_chr_socket_connected,
+ chr, NULL, chr->gcontext);
+}
+
static gboolean socket_reconnect_timeout(gpointer opaque)
{
Chardev *chr = CHARDEV(opaque);
SocketChardev *s = SOCKET_CHARDEV(opaque);
- QIOChannelSocket *sioc;
g_source_unref(s->reconnect_timer);
s->reconnect_timer = NULL;
@@ -868,11 +931,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
return false;
}
- sioc = qio_channel_socket_new();
- tcp_chr_set_client_ioc_name(chr, sioc);
- qio_channel_socket_connect_async(sioc, s->addr,
- qemu_chr_socket_connected,
- chr, NULL, NULL);
+ tcp_chr_connect_async(chr);
return false;
}
@@ -951,13 +1010,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
s->reconnect_time = reconnect;
}
- if (s->reconnect_time) {
- sioc = qio_channel_socket_new();
- tcp_chr_set_client_ioc_name(chr, sioc);
- qio_channel_socket_connect_async(sioc, s->addr,
- qemu_chr_socket_connected,
- chr, NULL, NULL);
- } else {
+ /* If reconnect_time is set, will do that in chr_machine_done. */
+ if (!s->reconnect_time) {
if (s->is_listen) {
char *name;
s->listener = qio_net_listener_new();
@@ -981,8 +1035,10 @@ static void qmp_chardev_open_socket(Chardev *chr,
return;
}
if (!s->ioc) {
- qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
- chr, NULL);
+ qio_net_listener_set_client_func_full(s->listener,
+ tcp_chr_accept,
+ chr, NULL,
+ chr->gcontext);
}
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
goto error;
@@ -1009,25 +1065,36 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
const char *path = qemu_opt_get(opts, "path");
const char *host = qemu_opt_get(opts, "host");
const char *port = qemu_opt_get(opts, "port");
+ const char *fd = qemu_opt_get(opts, "fd");
const char *tls_creds = qemu_opt_get(opts, "tls-creds");
SocketAddressLegacy *addr;
ChardevSocket *sock;
+ if ((!!path + !!fd + !!host) != 1) {
+ error_setg(errp,
+ "Exactly one of 'path', 'fd' or 'host' required");
+ return;
+ }
+
backend->type = CHARDEV_BACKEND_KIND_SOCKET;
- if (!path) {
- if (!host) {
- error_setg(errp, "chardev: socket: no host given");
+ if (path) {
+ if (tls_creds) {
+ error_setg(errp, "TLS can only be used over TCP socket");
return;
}
+ } else if (host) {
if (!port) {
error_setg(errp, "chardev: socket: no port given");
return;
}
- } else {
- if (tls_creds) {
- error_setg(errp, "TLS can only be used over TCP socket");
+ } else if (fd) {
+ /* We don't know what host to validate against when in client mode */
+ if (tls_creds && !is_listen) {
+ error_setg(errp, "TLS can not be used with pre-opened client FD");
return;
}
+ } else {
+ g_assert_not_reached();
}
sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
@@ -1053,7 +1120,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
q_unix->path = g_strdup(path);
- } else {
+ } else if (host) {
addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
addr->u.inet.data = g_new(InetSocketAddress, 1);
*addr->u.inet.data = (InetSocketAddress) {
@@ -1066,6 +1133,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
.has_ipv6 = qemu_opt_get(opts, "ipv6"),
.ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
};
+ } else if (fd) {
+ addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
+ addr->u.fd.data = g_new(String, 1);
+ addr->u.fd.data->str = g_strdup(fd);
+ } else {
+ g_assert_not_reached();
}
sock->addr = addr;
}
@@ -1087,6 +1160,21 @@ char_socket_get_connected(Object *obj, Error **errp)
return s->connected;
}
+static int tcp_chr_machine_done_hook(Chardev *chr)
+{
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+
+ if (s->reconnect_time) {
+ tcp_chr_connect_async(chr);
+ }
+
+ if (s->ioc && s->tls_creds) {
+ tcp_chr_tls_init(chr);
+ }
+
+ return 0;
+}
+
static void char_socket_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -1102,6 +1190,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
cc->chr_add_client = tcp_chr_add_client;
cc->chr_add_watch = tcp_chr_add_watch;
cc->chr_update_read_handler = tcp_chr_update_read_handler;
+ cc->chr_machine_done = tcp_chr_machine_done_hook;
object_class_property_add(oc, "addr", "SocketAddress",
char_socket_get_addr, NULL,
diff --git a/chardev/char.c b/chardev/char.c
index 5d7b079ef0..76d866e6fe 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -281,40 +281,31 @@ static const TypeInfo char_type_info = {
.class_init = char_class_init,
};
-/**
- * Called after processing of default and command-line-specified
- * chardevs to deliver CHR_EVENT_OPENED events to any FEs attached
- * to a mux chardev. This is done here to ensure that
- * output/prompts/banners are only displayed for the FE that has
- * focus when initial command-line processing/machine init is
- * completed.
- *
- * After this point, any new FE attached to any new or existing
- * mux will receive CHR_EVENT_OPENED notifications for the BE
- * immediately.
- */
-static int open_muxes(Object *child, void *opaque)
+static int chardev_machine_done_notify_one(Object *child, void *opaque)
{
- if (CHARDEV_IS_MUX(child)) {
- /* send OPENED to all already-attached FEs */
- mux_chr_send_all_event(CHARDEV(child), CHR_EVENT_OPENED);
- /* mark mux as OPENED so any new FEs will immediately receive
- * OPENED event
- */
- qemu_chr_be_event(CHARDEV(child), CHR_EVENT_OPENED);
+ Chardev *chr = (Chardev *)child;
+ ChardevClass *class = CHARDEV_GET_CLASS(chr);
+
+ if (class->chr_machine_done) {
+ return class->chr_machine_done(chr);
}
return 0;
}
-static void muxes_realize_done(Notifier *notifier, void *unused)
+static void chardev_machine_done_hook(Notifier *notifier, void *unused)
{
- muxes_realized = true;
- object_child_foreach(get_chardevs_root(), open_muxes, NULL);
+ int ret = object_child_foreach(get_chardevs_root(),
+ chardev_machine_done_notify_one, NULL);
+
+ if (ret) {
+ error_report("Failed to call chardev machine_done hooks");
+ exit(1);
+ }
}
-static Notifier muxes_realize_notify = {
- .notify = muxes_realize_done,
+static Notifier chardev_machine_done_notify = {
+ .notify = chardev_machine_done_hook,
};
static bool qemu_chr_is_busy(Chardev *s)
@@ -808,6 +799,9 @@ QemuOptsList qemu_chardev_opts = {
.name = "port",
.type = QEMU_OPT_STRING,
},{
+ .name = "fd",
+ .type = QEMU_OPT_STRING,
+ },{
.name = "localaddr",
.type = QEMU_OPT_STRING,
},{
@@ -1118,7 +1112,7 @@ static void register_types(void)
* as part of realize functions like serial_isa_realizefn when -nographic
* is specified
*/
- qemu_add_machine_init_done_notifier(&muxes_realize_notify);
+ qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
}
type_init(register_types);
diff --git a/configure b/configure
index af72fc852e..f156805981 100755
--- a/configure
+++ b/configure
@@ -342,7 +342,7 @@ attr=""
libattr=""
xfs=""
tcg="yes"
-
+membarrier=""
vhost_net="no"
vhost_crypto="no"
vhost_scsi="no"
@@ -1161,9 +1161,13 @@ for opt do
;;
--enable-attr) attr="yes"
;;
+ --disable-membarrier) membarrier="no"
+ ;;
+ --enable-membarrier) membarrier="yes"
+ ;;
--disable-blobs) blobs="no"
;;
- --with-pkgversion=*) pkgversion=" ($optarg)"
+ --with-pkgversion=*) pkgversion="$optarg"
;;
--with-coroutine=*) coroutine="$optarg"
;;
@@ -1577,6 +1581,7 @@ disabled with --disable-FEATURE, default is enabled if available:
xen-pci-passthrough
brlapi BrlAPI (Braile)
curl curl connectivity
+ membarrier membarrier system call (for Linux 4.14+ or Windows)
fdt fdt device tree
bluez bluez stack connectivity
kvm KVM acceleration support
@@ -5139,6 +5144,37 @@ if compile_prog "" "" ; then
fi
##########################################
+# check for usable membarrier system call
+if test "$membarrier" = "yes"; then
+ have_membarrier=no
+ if test "$mingw32" = "yes" ; then
+ have_membarrier=yes
+ elif test "$linux" = "yes" ; then
+ cat > $TMPC << EOF
+ #include <linux/membarrier.h>
+ #include <sys/syscall.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ int main(void) {
+ syscall(__NR_membarrier, MEMBARRIER_CMD_QUERY, 0);
+ syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0);
+ exit(0);
+ }
+EOF
+ if compile_prog "" "" ; then
+ have_membarrier=yes
+ fi
+ fi
+ if test "$have_membarrier" = "no"; then
+ feature_not_found "membarrier" "membarrier system call not available"
+ fi
+else
+ # Do not enable it by default even for Mingw32, because it doesn't
+ # work on Wine.
+ membarrier=no
+fi
+
+##########################################
# check if rtnetlink.h exists and is useful
have_rtnetlink=no
cat > $TMPC << EOF
@@ -5764,6 +5800,7 @@ fi
echo "malloc trim support $malloc_trim"
echo "RDMA support $rdma"
echo "fdt support $fdt"
+echo "membarrier $membarrier"
echo "preadv support $preadv"
echo "fdatasync $fdatasync"
echo "madvise $madvise"
@@ -6251,6 +6288,9 @@ fi
if test "$fdt" = "yes" ; then
echo "CONFIG_FDT=y" >> $config_host_mak
fi
+if test "$membarrier" = "yes" ; then
+ echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
+fi
if test "$signalfd" = "yes" ; then
echo "CONFIG_SIGNALFD=y" >> $config_host_mak
fi
@@ -6840,6 +6880,7 @@ case "$target_name" in
;;
xtensa|xtensaeb)
TARGET_ARCH=xtensa
+ mttcg="yes"
;;
*)
error_exit "Unsupported target CPU"
diff --git a/cpus.c b/cpus.c
index c652da84cf..2e6701795b 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1317,6 +1317,8 @@ static void prepare_icount_for_run(CPUState *cpu)
insns_left = MIN(0xffff, cpu->icount_budget);
cpu->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;
+
+ replay_mutex_lock();
}
}
@@ -1332,6 +1334,8 @@ static void process_icount_data(CPUState *cpu)
cpu->icount_budget = 0;
replay_account_executed_instructions();
+
+ replay_mutex_unlock();
}
}
@@ -1346,11 +1350,9 @@ static int tcg_cpu_exec(CPUState *cpu)
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
- qemu_mutex_unlock_iothread();
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
- qemu_mutex_lock_iothread();
#ifdef CONFIG_PROFILER
tcg_time += profile_getclock() - ti;
#endif
@@ -1417,6 +1419,9 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
cpu->exit_request = 1;
while (1) {
+ qemu_mutex_unlock_iothread();
+ replay_mutex_lock();
+ qemu_mutex_lock_iothread();
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
qemu_account_warp_timer();
@@ -1425,6 +1430,8 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
*/
handle_icount_deadline();
+ replay_mutex_unlock();
+
if (!cpu) {
cpu = first_cpu;
}
@@ -1440,11 +1447,13 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
if (cpu_can_run(cpu)) {
int r;
+ qemu_mutex_unlock_iothread();
prepare_icount_for_run(cpu);
r = tcg_cpu_exec(cpu);
process_icount_data(cpu);
+ qemu_mutex_lock_iothread();
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
@@ -1634,7 +1643,9 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
while (1) {
if (cpu_can_run(cpu)) {
int r;
+ qemu_mutex_unlock_iothread();
r = tcg_cpu_exec(cpu);
+ qemu_mutex_lock_iothread();
switch (r) {
case EXCP_DEBUG:
cpu_handle_guest_debug(cpu);
@@ -1781,12 +1792,21 @@ void pause_all_vcpus(void)
}
}
+ /* We need to drop the replay_lock so any vCPU threads woken up
+ * can finish their replay tasks
+ */
+ replay_mutex_unlock();
+
while (!all_vcpus_paused()) {
qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
CPU_FOREACH(cpu) {
qemu_cpu_kick(cpu);
}
}
+
+ qemu_mutex_unlock_iothread();
+ replay_mutex_lock();
+ qemu_mutex_lock_iothread();
}
void cpu_resume(CPUState *cpu)
diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak
index e0d75e3058..bbe361f01a 100644
--- a/default-configs/alpha-softmmu.mak
+++ b/default-configs/alpha-softmmu.mak
@@ -4,7 +4,12 @@ include pci.mak
include usb.mak
CONFIG_SERIAL=y
CONFIG_SERIAL_ISA=y
+CONFIG_I82374=y
CONFIG_I8254=y
+CONFIG_I8257=y
+CONFIG_PARALLEL=y
+CONFIG_PARALLEL_ISA=y
+CONFIG_FDC=y
CONFIG_PCKBD=y
CONFIG_VGA_CIRRUS=y
CONFIG_IDE_CORE=y
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 3326e3e0bb..8c7d4a0fa0 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -63,3 +63,6 @@ CONFIG_PXB=y
CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
+CONFIG_SEV=$(CONFIG_KVM)
+CONFIG_VTD=y
+CONFIG_AMD_IOMMU=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 1c6cda1d9a..0390b4303c 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -63,3 +63,6 @@ CONFIG_PXB=y
CONFIG_ACPI_VMGENID=y
CONFIG_FW_CFG_DMA=y
CONFIG_I2C=y
+CONFIG_SEV=$(CONFIG_KVM)
+CONFIG_VTD=y
+CONFIG_AMD_IOMMU=y
diff --git a/default-configs/xtensa-linux-user.mak b/default-configs/xtensa-linux-user.mak
new file mode 100644
index 0000000000..fd1d350ee9
--- /dev/null
+++ b/default-configs/xtensa-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for xtensa-linux-user
diff --git a/default-configs/xtensaeb-linux-user.mak b/default-configs/xtensaeb-linux-user.mak
new file mode 100644
index 0000000000..fd1d350ee9
--- /dev/null
+++ b/default-configs/xtensaeb-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for xtensa-linux-user
diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt
new file mode 100644
index 0000000000..f483795eaa
--- /dev/null
+++ b/docs/amd-memory-encryption.txt
@@ -0,0 +1,109 @@
+Secure Encrypted Virtualization (SEV) is a feature found on AMD processors.
+
+SEV is an extension to the AMD-V architecture which supports running encrypted
+virtual machine (VMs) under the control of KVM. Encrypted VMs have their pages
+(code and data) secured such that only the guest itself has access to the
+unencrypted version. Each encrypted VM is associated with a unique encryption
+key; if its data is accessed to a different entity using a different key the
+encrypted guests data will be incorrectly decrypted, leading to unintelligible
+data.
+
+The key management of this feature is handled by separate processor known as
+AMD secure processor (AMD-SP) which is present in AMD SOCs. Firmware running
+inside the AMD-SP provide commands to support common VM lifecycle. This
+includes commands for launching, snapshotting, migrating and debugging the
+encrypted guest. Those SEV command can be issued via KVM_MEMORY_ENCRYPT_OP
+ioctls.
+
+Launching
+---------
+Boot images (such as bios) must be encrypted before guest can be booted.
+MEMORY_ENCRYPT_OP ioctl provides commands to encrypt the images :LAUNCH_START,
+LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands
+together generate a fresh memory encryption key for the VM, encrypt the boot
+images and provide a measurement than can be used as an attestation of the
+successful launch.
+
+LAUNCH_START is called first to create a cryptographic launch context within
+the firmware. To create this context, guest owner must provides guest policy,
+its public Diffie-Hellman key (PDH) and session parameters. These inputs
+should be treated as binary blob and must be passed as-is to the SEV firmware.
+
+The guest policy is passed as plaintext and hypervisor may able to read it
+but should not modify it (any modification of the policy bits will result
+in bad measurement). The guest policy is a 4-byte data structure containing
+several flags that restricts what can be done on running SEV guest.
+See KM Spec section 3 and 6.2 for more details.
+
+The guest policy can be provided via the 'policy' property (see below)
+
+# ${QEMU} \
+ sev-guest,id=sev0,policy=0x1...\
+
+Guest owners provided DH certificate and session parameters will be used to
+establish a cryptographic session with the guest owner to negotiate keys used
+for the attestation.
+
+The DH certificate and session blob can be provided via 'dh-cert-file' and
+'session-file' property (see below
+
+# ${QEMU} \
+ sev-guest,id=sev0,dh-cert-file=<file1>,session-file=<file2>
+
+LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context
+created via LAUNCH_START command. If required, this command can be called
+multiple times to encrypt different memory regions. The command also calculates
+the measurement of the memory contents as it encrypts.
+
+LAUNCH_MEASURE command can be used to retrieve the measurement of encrypted
+memory. This measurement is a signature of the memory contents that can be
+sent to the guest owner as an attestation that the memory was encrypted
+correctly by the firmware. The guest owner may wait to provide the guest
+confidential information until it can verify the attestation measurement.
+Since the guest owner knows the initial contents of the guest at boot, the
+attestation measurement can be verified by comparing it to what the guest owner
+expects.
+
+LAUNCH_FINISH command finalizes the guest launch and destroy's the cryptographic
+context.
+
+See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the
+complete flow chart.
+
+To launch a SEV guest
+
+# ${QEMU} \
+ -machine ...,memory-encryption=sev0 \
+ -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1
+
+Debugging
+-----------
+Since memory contents of SEV guest is encrypted hence hypervisor access to the
+guest memory will get a cipher text. If guest policy allows debugging, then
+hypervisor can use DEBUG_DECRYPT and DEBUG_ENCRYPT commands access the guest
+memory region for debug purposes. This is not supported in QEMU yet.
+
+Snapshot/Restore
+-----------------
+TODO
+
+Live Migration
+----------------
+TODO
+
+References
+-----------------
+
+AMD Memory Encryption whitepaper:
+http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf
+
+Secure Encrypted Virutualization Key Management:
+[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf
+
+KVM Forum slides:
+http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf
+
+AMD64 Architecture Programmer's Manual:
+ http://support.amd.com/TechDocs/24593.pdf
+ SME is section 7.10
+ SEV is section 15.34
diff --git a/docs/devel/atomics.txt b/docs/devel/atomics.txt
index 10c5fa37e8..a4db3a4aaa 100644
--- a/docs/devel/atomics.txt
+++ b/docs/devel/atomics.txt
@@ -122,20 +122,30 @@ In general, if the algorithm you are writing includes both writes
and reads on the same side, it is generally simpler to use sequentially
consistent primitives.
-When using this model, variables are accessed with atomic_read() and
-atomic_set(), and restrictions to the ordering of accesses is enforced
+When using this model, variables are accessed with:
+
+- atomic_read() and atomic_set(); these prevent the compiler from
+ optimizing accesses out of existence and creating unsolicited
+ accesses, but do not otherwise impose any ordering on loads and
+ stores: both the compiler and the processor are free to reorder
+ them.
+
+- atomic_load_acquire(), which guarantees the LOAD to appear to
+ happen, with respect to the other components of the system,
+ before all the LOAD or STORE operations specified afterwards.
+ Operations coming before atomic_load_acquire() can still be
+ reordered after it.
+
+- atomic_store_release(), which guarantees the STORE to appear to
+ happen, with respect to the other components of the system,
+ after all the LOAD or STORE operations specified afterwards.
+ Operations coming after atomic_store_release() can still be
+ reordered after it.
+
+Restrictions to the ordering of accesses can also be specified
using the memory barrier macros: smp_rmb(), smp_wmb(), smp_mb(),
smp_mb_acquire(), smp_mb_release(), smp_read_barrier_depends().
-atomic_read() and atomic_set() prevents the compiler from using
-optimizations that might otherwise optimize accesses out of existence
-on the one hand, or that might create unsolicited accesses on the other.
-In general this should not have any effect, because the same compiler
-barriers are already implied by memory barriers. However, it is useful
-to do so, because it tells readers which variables are shared with
-other threads, and which are local to the current thread or protected
-by other, more mundane means.
-
Memory barriers control the order of references to shared memory.
They come in six kinds:
@@ -232,7 +242,7 @@ make atomic_mb_set() the more expensive operation.
There are two common cases in which atomic_mb_read and atomic_mb_set
generate too many memory barriers, and thus it can be useful to manually
-place barriers instead:
+place barriers, or use atomic_load_acquire/atomic_store_release instead:
- when a data structure has one thread that is always a writer
and one thread that is always a reader, manual placement of
@@ -243,18 +253,15 @@ place barriers instead:
thread 1 thread 1
------------------------- ------------------------
(other writes)
- smp_mb_release()
- atomic_mb_set(&a, x) atomic_set(&a, x)
- smp_wmb()
- atomic_mb_set(&b, y) atomic_set(&b, y)
+ atomic_mb_set(&a, x) atomic_store_release(&a, x)
+ atomic_mb_set(&b, y) atomic_store_release(&b, y)
=>
thread 2 thread 2
------------------------- ------------------------
- y = atomic_mb_read(&b) y = atomic_read(&b)
- smp_rmb()
- x = atomic_mb_read(&a) x = atomic_read(&a)
- smp_mb_acquire()
+ y = atomic_mb_read(&b) y = atomic_load_acquire(&b)
+ x = atomic_mb_read(&a) x = atomic_load_acquire(&a)
+ (other reads)
Note that the barrier between the stores in thread 1, and between
the loads in thread 2, has been optimized here to a write or a
@@ -276,7 +283,6 @@ place barriers instead:
smp_mb_acquire();
Similarly, atomic_mb_set() can be transformed as follows:
- smp_mb():
smp_mb_release();
for (i = 0; i < 10; i++) => for (i = 0; i < 10; i++)
@@ -284,6 +290,8 @@ place barriers instead:
smp_mb();
+ The other thread can still use atomic_mb_read()/atomic_mb_set().
+
The two tricks can be combined. In this case, splitting a loop in
two lets you hoist the barriers out of the loops _and_ eliminate the
expensive smp_mb():
@@ -296,8 +304,6 @@ expensive smp_mb():
atomic_set(&a[i], false);
smp_mb();
- The other thread can still use atomic_mb_read()/atomic_mb_set()
-
Memory barrier pairing
----------------------
@@ -386,10 +392,7 @@ and memory barriers, and the equivalents in QEMU:
note that smp_store_mb() is a little weaker than atomic_mb_set().
atomic_mb_read() compiles to the same instructions as Linux's
smp_load_acquire(), but this should be treated as an implementation
- detail. QEMU does have atomic_load_acquire() and atomic_store_release()
- macros, but for now they are only used within atomic.h. This may
- change in the future.
-
+ detail.
SOURCES
=======
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 25b7180a18..a569d24745 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -554,9 +554,12 @@ following example objects:
=== Commands ===
+--- General Command Layout ---
+
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
'*returns': TYPE-NAME, '*boxed': true,
- '*gen': false, '*success-response': false }
+ '*gen': false, '*success-response': false,
+ '*allow-oob': true }
Commands are defined by using a dictionary containing several members,
where three members are most common. The 'command' member is a
@@ -636,6 +639,49 @@ possible, the command expression should include the optional key
'success-response' with boolean value false. So far, only QGA makes
use of this member.
+A command can be declared to support Out-Of-Band (OOB) execution. By
+default, commands do not support OOB. To declare a command that
+supports it, the schema includes an extra 'allow-oob' field. For
+example:
+
+ { 'command': 'migrate_recover',
+ 'data': { 'uri': 'str' }, 'allow-oob': true }
+
+To execute a command with out-of-band priority, the client specifies
+the "control" field in the request, with "run-oob" set to
+true. Example:
+
+ => { "execute": "command-support-oob",
+ "arguments": { ... },
+ "control": { "run-oob": true } }
+ <= { "return": { } }
+
+Without it, even the commands that support out-of-band execution will
+still be run in-band.
+
+Under normal QMP command execution, the following apply to each
+command:
+
+- They are executed in order,
+- They run only in main thread of QEMU,
+- They have the BQL taken during execution.
+
+When a command is executed with OOB, the following changes occur:
+
+- They can be completed before a pending in-band command,
+- They run in a dedicated monitor thread,
+- They do not take the BQL during execution.
+
+OOB command handlers must satisfy the following conditions:
+
+- It executes extremely fast,
+- It does not take any lock, or, it can take very small locks if all
+ critical regions also follow the rules for OOB command handler code,
+- It does not invoke system calls that may block,
+- It does not access guest RAM that may block when userfaultfd is
+ enabled for postcopy live migration.
+
+If in doubt, do not implement OOB execution support.
=== Events ===
@@ -739,10 +785,12 @@ references by name.
QAPI schema definitions not reachable that way are omitted.
The SchemaInfo for a command has meta-type "command", and variant
-members "arg-type" and "ret-type". On the wire, the "arguments"
-member of a client's "execute" command must conform to the object type
-named by "arg-type". The "return" member that the server passes in a
-success response conforms to the type named by "ret-type".
+members "arg-type", "ret-type" and "allow-oob". On the wire, the
+"arguments" member of a client's "execute" command must conform to the
+object type named by "arg-type". The "return" member that the server
+passes in a success response conforms to the type named by
+"ret-type". When "allow-oob" is set, it means the command supports
+out-of-band execution.
If the command takes no arguments, "arg-type" names an object type
without members. Likewise, if the command returns nothing, "ret-type"
@@ -1319,18 +1367,27 @@ Example:
#ifndef EXAMPLE_QMP_INTROSPECT_H
#define EXAMPLE_QMP_INTROSPECT_H
- extern const char example_qmp_schema_json[];
+ extern const QLitObject qmp_schema_qlit;
#endif
$ cat qapi-generated/example-qapi-introspect.c
[Uninteresting stuff omitted...]
- const char example_qmp_schema_json[] = "["
- "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
- "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
- "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
- "{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
- "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
- "{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
- "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
- "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
+ const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) {
+ QLIT_QDICT(((QLitDictEntry[]) {
+ { "arg-type", QLIT_QSTR("0") },
+ { "meta-type", QLIT_QSTR("event") },
+ { "name", QLIT_QSTR("Event") },
+ { }
+ })),
+ QLIT_QDICT(((QLitDictEntry[]) {
+ { "members", QLIT_QLIST(((QLitObject[]) {
+ { }
+ })) },
+ { "meta-type", QLIT_QSTR("object") },
+ { "name", QLIT_QSTR("0") },
+ { }
+ })),
+ ...
+ { }
+ }));
diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt
index f8b5356015..6fa193a80b 100644
--- a/docs/interop/qmp-spec.txt
+++ b/docs/interop/qmp-spec.txt
@@ -83,16 +83,27 @@ The greeting message format is:
2.2.1 Capabilities
------------------
-As of the date this document was last revised, no server or client
-capability strings have been defined.
+Currently supported capabilities are:
+- "oob": the QMP server supports "Out-Of-Band" (OOB) command
+ execution. For more details, please see the "run-oob" parameter in
+ the "Issuing Commands" section below. Not all commands allow this
+ "oob" execution. The "query-qmp-schema" command can be used to
+ inspect which commands support "oob" execution.
+
+QMP clients can get a list of supported QMP capabilities of the QMP
+server in the greeting message mentioned above. By default, all the
+capabilities are off. To enable any QMP capabilities, the QMP client
+needs to send the "qmp_capabilities" command with an extra parameter
+for the requested capabilities.
2.3 Issuing Commands
--------------------
The format for command execution is:
-{ "execute": json-string, "arguments": json-object, "id": json-value }
+{ "execute": json-string, "arguments": json-object, "id": json-value,
+ "control": json-object }
Where,
@@ -102,10 +113,16 @@ The format for command execution is:
required. Each command documents what contents will be considered
valid when handling the json-argument
- The "id" member is a transaction identification associated with the
- command execution, it is optional and will be part of the response if
- provided. The "id" member can be any json-value, although most
- clients merely use a json-number incremented for each successive
- command
+ command execution. It is required for all commands if the OOB -
+ capability was enabled at startup, and optional otherwise. The same
+ "id" field will be part of the response if provided. The "id" member
+ can be any json-value, although most clients merely use a
+ json-number incremented for each successive command
+- The "control" member is optional, and currently only used for
+ out-of-band execution. The handling or response of an "oob" command
+ can overtake prior in-band commands. To enable "oob" handling of a
+ particular command, just provide a control field with: { "control":
+ { "run-oob": true } }
2.4 Commands Responses
----------------------
@@ -113,6 +130,11 @@ The format for command execution is:
There are two possible responses which the Server will issue as the result
of a command execution: success or error.
+As long as the commands were issued with a proper "id" field, then the
+same "id" field will be attached in the corresponding response message
+so that requests and responses can match. Clients should drop all the
+responses that have an unknown "id" field.
+
2.4.1 success
-------------
diff --git a/docs/replay.txt b/docs/replay.txt
index 486c1e0e9d..2e21e9ccb0 100644
--- a/docs/replay.txt
+++ b/docs/replay.txt
@@ -7,14 +7,10 @@ See the COPYING file in the top-level directory.
Record/replay
-------------
-Record/replay functions are used for the reverse execution and deterministic
-replay of qemu execution. This implementation of deterministic replay can
-be used for deterministic debugging of guest code through a gdb remote
-interface.
-
+Record/replay functions are used for the deterministic replay of qemu execution.
Execution recording writes a non-deterministic events log, which can be later
used for replaying the execution anywhere and for unlimited number of times.
-It also supports checkpointing for faster rewinding during reverse debugging.
+It also supports checkpointing for faster rewind to the specific replay moment.
Execution replaying reads the log and replays all non-deterministic events
including external input, hardware clocks, and interrupts.
@@ -28,16 +24,36 @@ Deterministic replay has the following features:
input devices.
Usage of the record/replay:
- * First, record the execution, by adding the following arguments to the command line:
- '-icount shift=7,rr=record,rrfile=replay.bin -net none'.
- Block devices' images are not actually changed in the recording mode,
+ * First, record the execution with the following command line:
+ qemu-system-i386 \
+ -icount shift=7,rr=record,rrfile=replay.bin \
+ -drive file=disk.qcow2,if=none,id=img-direct \
+ -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
+ -device ide-hd,drive=img-blkreplay \
+ -netdev user,id=net1 -device rtl8139,netdev=net1 \
+ -object filter-replay,id=replay,netdev=net1
+ * After recording, you can replay it by using another command line:
+ qemu-system-i386 \
+ -icount shift=7,rr=replay,rrfile=replay.bin \
+ -drive file=disk.qcow2,if=none,id=img-direct \
+ -drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
+ -device ide-hd,drive=img-blkreplay \
+ -netdev user,id=net1 -device rtl8139,netdev=net1 \
+ -object filter-replay,id=replay,netdev=net1
+ The only difference with recording is changing the rr option
+ from record to replay.
+ * Block device images are not actually changed in the recording mode,
because all of the changes are written to the temporary overlay file.
- * Then you can replay it by using another command
- line option: '-icount shift=7,rr=replay,rrfile=replay.bin -net none'
- * '-net none' option should also be specified if network replay patches
- are not applied.
-
-Papers with description of deterministic replay implementation:
+ This behavior is enabled by using blkreplay driver. It should be used
+ for every enabled block device, as described in 'Block devices' section.
+ * '-net none' option should be specified when network is not used,
+ because QEMU adds network card by default. When network is needed,
+ it should be configured explicitly with replay filter, as described
+ in 'Network devices' section.
+ * Interaction with audio devices and serial ports are recorded and replayed
+ automatically when such devices are enabled.
+
+Academic papers with description of deterministic replay implementation:
http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html
http://dl.acm.org/citation.cfm?id=2786805.2803179
@@ -46,8 +62,33 @@ Modifications of qemu include:
* saving different asynchronous events (e.g. system shutdown) into the log
* synchronization of the bottom halves execution
* synchronization of the threads from thread pool
- * recording/replaying user input (mouse and keyboard)
+ * recording/replaying user input (mouse, keyboard, and microphone)
* adding internal checkpoints for cpu and io synchronization
+ * network filter for recording and replaying the packets
+ * block driver for making block layer deterministic
+ * serial port input record and replay
+
+Locking and thread synchronisation
+----------------------------------
+
+Previously the synchronisation of the main thread and the vCPU thread
+was ensured by the holding of the BQL. However the trend has been to
+reduce the time the BQL was held across the system including under TCG
+system emulation. As it is important that batches of events are kept
+in sequence (e.g. expiring timers and checkpoints in the main thread
+while instruction checkpoints are written by the vCPU thread) we need
+another lock to keep things in lock-step. This role is now handled by
+the replay_mutex_lock. It used to be held only for each event being
+written but now it is held for a whole execution period. This results
+in a deterministic ping-pong between the two main threads.
+
+As the BQL is now a finer grained lock than the replay_lock it is almost
+certainly a bug, and a source of deadlocks, to take the
+replay_mutex_lock while the BQL is held. This is enforced by an assert.
+While the unlocks are usually in the reverse order, this is not
+necessary; you can drop the replay_lock while holding the BQL, without
+doing a more complicated unlock_iothread/replay_unlock/lock_iothread
+sequence.
Non-deterministic events
------------------------
@@ -55,12 +96,11 @@ Non-deterministic events
Our record/replay system is based on saving and replaying non-deterministic
events (e.g. keyboard input) and simulating deterministic ones (e.g. reading
from HDD or memory of the VM). Saving only non-deterministic events makes
-log file smaller, simulation faster, and allows using reverse debugging even
-for realtime applications.
+log file smaller and simulation faster.
The following non-deterministic data from peripheral devices is saved into
the log: mouse and keyboard input, network packets, audio controller input,
-USB packets, serial port input, and hardware clocks (they are non-deterministic
+serial port input, and hardware clocks (they are non-deterministic
too, because their values are taken from the host machine). Inputs from
simulated hardware, memory of VM, software interrupts, and execution of
instructions are not saved into the log, because they are deterministic and
@@ -183,7 +223,7 @@ Block devices record/replay module intercepts calls of
bdrv coroutine functions at the top of block drivers stack.
To record and replay block operations the drive must be configured
as following:
- -drive file=disk.qcow,if=none,id=img-direct
+ -drive file=disk.qcow2,if=none,id=img-direct
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
-device ide-hd,drive=img-blkreplay
@@ -212,6 +252,12 @@ This snapshot is created at start of recording and restored at start
of replaying. It also can be loaded while replaying to roll back
the execution.
+Use QEMU monitor to create additional snapshots. 'savevm <name>' command
+created the snapshot and 'loadvm <name>' restores it. To prevent corruption
+of the original disk image, use overlay files linked to the original images.
+Therefore all new snapshots (including the starting one) will be saved in
+overlays and the original image remains unchanged.
+
Network devices
---------------
@@ -232,3 +278,80 @@ Audio devices
Audio data is recorded and replay automatically. The command line for recording
and replaying must contain identical specifications of audio hardware, e.g.:
-soundhw ac97
+
+Serial ports
+------------
+
+Serial ports input is recorded and replay automatically. The command lines
+for recording and replaying must contain identical number of ports in record
+and replay modes, but their backends may differ.
+E.g., '-serial stdio' in record mode, and '-serial null' in replay mode.
+
+Replay log format
+-----------------
+
+Record/replay log consits of the header and the sequence of execution
+events. The header includes 4-byte replay version id and 8-byte reserved
+field. Version is updated every time replay log format changes to prevent
+using replay log created by another build of qemu.
+
+The sequence of the events describes virtual machine state changes.
+It includes all non-deterministic inputs of VM, synchronization marks and
+instruction counts used to correctly inject inputs at replay.
+
+Synchronization marks (checkpoints) are used for synchronizing qemu threads
+that perform operations with virtual hardware. These operations may change
+system's state (e.g., change some register or generate interrupt) and
+therefore should execute synchronously with CPU thread.
+
+Every event in the log includes 1-byte event id and optional arguments.
+When argument is an array, it is stored as 4-byte array length
+and corresponding number of bytes with data.
+Here is the list of events that are written into the log:
+
+ - EVENT_INSTRUCTION. Instructions executed since last event.
+ Argument: 4-byte number of executed instructions.
+ - EVENT_INTERRUPT. Used to synchronize interrupt processing.
+ - EVENT_EXCEPTION. Used to synchronize exception handling.
+ - EVENT_ASYNC. This is a group of events. They are always processed
+ together with checkpoints. When such an event is generated, it is
+ stored in the queue and processed only when checkpoint occurs.
+ Every such event is followed by 1-byte checkpoint id and 1-byte
+ async event id from the following list:
+ - REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes
+ callbacks that affect virtual machine state, but normally called
+ asyncronously.
+ Argument: 8-byte operation id.
+ - REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains
+ parameters of keyboard and mouse input operations
+ (key press/release, mouse pointer movement).
+ Arguments: 9-16 bytes depending of input event.
+ - REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event.
+ - REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input
+ initiated by the sender.
+ Arguments: 1-byte character device id.
+ Array with bytes were read.
+ - REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize
+ operations with disk and flash drives with CPU.
+ Argument: 8-byte operation id.
+ - REPLAY_ASYNC_EVENT_NET. Incoming network packet.
+ Arguments: 1-byte network adapter id.
+ 4-byte packet flags.
+ Array with packet bytes.
+ - EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu,
+ e.g., by closing the window.
+ - EVENT_CHAR_WRITE. Used to synchronize character output operations.
+ Arguments: 4-byte output function return value.
+ 4-byte offset in the output array.
+ - EVENT_CHAR_READ_ALL. Used to synchronize character input operations,
+ initiated by qemu.
+ Argument: Array with bytes that were read.
+ - EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation,
+ initiated by qemu.
+ Argument: 4-byte error code.
+ - EVENT_CLOCK + clock_id. Group of events for host clock read operations.
+ Argument: 8-byte clock value.
+ - EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of
+ CPU, internal threads, and asynchronous input events. May be followed
+ by one or more EVENT_ASYNC events.
+ - EVENT_END. Last event in the log.
diff --git a/dump.c b/dump.c
index 097e60b2b3..6bdb0dbe23 100644
--- a/dump.c
+++ b/dump.c
@@ -1609,10 +1609,18 @@ static void vmcoreinfo_update_phys_base(DumpState *s)
lines = g_strsplit((char *)vmci, "\n", -1);
for (i = 0; lines[i]; i++) {
- if (g_str_has_prefix(lines[i], "NUMBER(phys_base)=")) {
- if (qemu_strtou64(lines[i] + 18, NULL, 16,
+ const char *prefix = NULL;
+
+ if (s->dump_info.d_machine == EM_X86_64) {
+ prefix = "NUMBER(phys_base)=";
+ } else if (s->dump_info.d_machine == EM_AARCH64) {
+ prefix = "NUMBER(PHYS_OFFSET)=";
+ }
+
+ if (prefix && g_str_has_prefix(lines[i], prefix)) {
+ if (qemu_strtou64(lines[i] + strlen(prefix), NULL, 16,
&phys_base) < 0) {
- warn_report("Failed to read NUMBER(phys_base)=");
+ warn_report("Failed to read %s", prefix);
} else {
s->dump_info.phys_base = phys_base;
}
diff --git a/exec.c b/exec.c
index 0eb890dca8..c09bd93df3 100644
--- a/exec.c
+++ b/exec.c
@@ -822,6 +822,29 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
#endif
}
+const char *parse_cpu_model(const char *cpu_model)
+{
+ ObjectClass *oc;
+ CPUClass *cc;
+ gchar **model_pieces;
+ const char *cpu_type;
+
+ model_pieces = g_strsplit(cpu_model, ",", 2);
+
+ oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);
+ if (oc == NULL) {
+ error_report("unable to find CPU model '%s'", model_pieces[0]);
+ g_strfreev(model_pieces);
+ exit(EXIT_FAILURE);
+ }
+
+ cpu_type = object_class_get_name(oc);
+ cc = CPU_CLASS(oc);
+ cc->parse_features(cpu_type, model_pieces[1], &error_fatal);
+ g_strfreev(model_pieces);
+ return cpu_type;
+}
+
#if defined(CONFIG_USER_ONLY)
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
{
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index ad590a4ffb..ddfcd5adcc 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -867,6 +867,22 @@ Display the amount of initially allocated and present hotpluggable (if
enabled) memory in bytes.
ETEXI
+#if defined(TARGET_I386)
+ {
+ .name = "sev",
+ .args_type = "",
+ .params = "",
+ .help = "show SEV information",
+ .cmd = hmp_info_sev,
+ },
+#endif
+
+STEXI
+@item info sev
+@findex info sev
+Show SEV information.
+ETEXI
+
STEXI
@end table
ETEXI
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 1723cbe1df..35d862a5d2 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -106,7 +106,8 @@ ETEXI
.args_type = "force:-f,device:B",
.params = "[-f] device",
.help = "stop an active background block operation (use -f"
- "\n\t\t\t if the operation is currently paused)",
+ "\n\t\t\t if you want to abort the operation immediately"
+ "\n\t\t\t instead of keep running until data is in sync)",
.cmd = hmp_block_job_cancel,
},
diff --git a/hmp.h b/hmp.h
index b89733876d..4e2ec375b0 100644
--- a/hmp.h
+++ b/hmp.h
@@ -143,5 +143,6 @@ void hmp_info_ramblock(Monitor *mon, const QDict *qdict);
void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
+void hmp_info_sev(Monitor *mon, const QDict *qdict);
#endif
diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index 766373eec7..80b987f7fb 100644
--- a/hw/alpha/dp264.c
+++ b/hw/alpha/dp264.c
@@ -19,7 +19,8 @@
#include "hw/timer/mc146818rtc.h"
#include "hw/ide.h"
#include "hw/timer/i8254.h"
-#include "hw/char/serial.h"
+#include "hw/isa/superio.h"
+#include "hw/dma/i8257.h"
#include "qemu/cutils.h"
#define MAX_IDE_BUS 2
@@ -81,19 +82,21 @@ static void clipper_init(MachineState *machine)
mc146818_rtc_init(isa_bus, 1900, rtc_irq);
i8254_pit_init(isa_bus, 0x40, 0, NULL);
- isa_create_simple(isa_bus, "i8042");
/* VGA setup. Don't bother loading the bios. */
pci_vga_init(pci_bus);
- /* Serial code setup. */
- serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
-
/* Network setup. e1000 is good enough, failing Tulip support. */
for (i = 0; i < nb_nics; i++) {
pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
}
+ /* 2 82C37 (dma) */
+ isa_create_simple(isa_bus, "i82374");
+
+ /* Super I/O */
+ isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO);
+
/* IDE disk setup. */
{
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c
index 40e8b25a46..3e7e8ca791 100644
--- a/hw/arm/bcm2836.c
+++ b/hw/arm/bcm2836.c
@@ -23,9 +23,40 @@
/* "QA7" (Pi2) interrupt controller and mailboxes etc. */
#define BCM2836_CONTROL_BASE 0x40000000
+struct BCM283XInfo {
+ const char *name;
+ const char *cpu_type;
+ int clusterid;
+};
+
+static const BCM283XInfo bcm283x_socs[] = {
+ {
+ .name = TYPE_BCM2836,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"),
+ .clusterid = 0xf,
+ },
+#ifdef TARGET_AARCH64
+ {
+ .name = TYPE_BCM2837,
+ .cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"),
+ .clusterid = 0x0,
+ },
+#endif
+};
+
static void bcm2836_init(Object *obj)
{
- BCM2836State *s = BCM2836(obj);
+ BCM283XState *s = BCM283X(obj);
+ BCM283XClass *bc = BCM283X_GET_CLASS(obj);
+ const BCM283XInfo *info = bc->info;
+ int n;
+
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
+ info->cpu_type);
+ object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
+ &error_abort);
+ }
object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL);
object_property_add_child(obj, "control", OBJECT(&s->control), NULL);
@@ -44,21 +75,15 @@ static void bcm2836_init(Object *obj)
static void bcm2836_realize(DeviceState *dev, Error **errp)
{
- BCM2836State *s = BCM2836(dev);
+ BCM283XState *s = BCM283X(dev);
+ BCM283XClass *bc = BCM283X_GET_CLASS(dev);
+ const BCM283XInfo *info = bc->info;
Object *obj;
Error *err = NULL;
int n;
/* common peripherals from bcm2835 */
- obj = OBJECT(dev);
- for (n = 0; n < BCM2836_NCPUS; n++) {
- object_initialize(&s->cpus[n], sizeof(s->cpus[n]),
- s->cpu_type);
- object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]),
- &error_abort);
- }
-
obj = object_property_get_link(OBJECT(dev), "ram", &err);
if (obj == NULL) {
error_setg(errp, "%s: required ram link not found: %s",
@@ -102,11 +127,9 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1,
qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-fiq", 0));
- for (n = 0; n < BCM2836_NCPUS; n++) {
- /* Mirror bcm2836, which has clusterid set to 0xf
- * TODO: this should be converted to a property of ARM_CPU
- */
- s->cpus[n].mp_affinity = 0xF00 | n;
+ for (n = 0; n < BCM283X_NCPUS; n++) {
+ /* TODO: this should be converted to a property of ARM_CPU */
+ s->cpus[n].mp_affinity = (info->clusterid << 8) | n;
/* set periphbase/CBAR value for CPU-local registers */
object_property_set_int(OBJECT(&s->cpus[n]),
@@ -150,30 +173,44 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
}
static Property bcm2836_props[] = {
- DEFINE_PROP_STRING("cpu-type", BCM2836State, cpu_type),
- DEFINE_PROP_UINT32("enabled-cpus", BCM2836State, enabled_cpus, BCM2836_NCPUS),
+ DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus,
+ BCM283X_NCPUS),
DEFINE_PROP_END_OF_LIST()
};
-static void bcm2836_class_init(ObjectClass *oc, void *data)
+static void bcm283x_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ BCM283XClass *bc = BCM283X_CLASS(oc);
- dc->props = bcm2836_props;
+ bc->info = data;
dc->realize = bcm2836_realize;
+ dc->props = bcm2836_props;
}
-static const TypeInfo bcm2836_type_info = {
- .name = TYPE_BCM2836,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(BCM2836State),
+static const TypeInfo bcm283x_type_info = {
+ .name = TYPE_BCM283X,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(BCM283XState),
.instance_init = bcm2836_init,
- .class_init = bcm2836_class_init,
+ .class_size = sizeof(BCM283XClass),
+ .abstract = true,
};
static void bcm2836_register_types(void)
{
- type_register_static(&bcm2836_type_info);
+ int i;
+
+ type_register_static(&bcm283x_type_info);
+ for (i = 0; i < ARRAY_SIZE(bcm283x_socs); i++) {
+ TypeInfo ti = {
+ .name = bcm283x_socs[i].name,
+ .parent = TYPE_BCM283X,
+ .class_init = bcm283x_class_init,
+ .class_data = (void *) &bcm283x_socs[i],
+ };
+ type_register(&ti);
+ }
}
type_init(bcm2836_register_types)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 196c7fb242..9319b12fcd 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -720,6 +720,18 @@ static void do_cpu_reset(void *opaque)
} else {
env->pstate = PSTATE_MODE_EL1h;
}
+ /* AArch64 kernels never boot in secure mode */
+ assert(!info->secure_boot);
+ /* This hook is only supported for AArch32 currently:
+ * bootloader_aarch64[] will not call the hook, and
+ * the code above has already dropped us into EL2 or EL1.
+ */
+ assert(!info->secure_board_setup);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ /* If we have EL2 then Linux expects the HVC insn to work */
+ env->cp15.scr_el3 |= SCR_HCE;
}
/* Set to non-secure if not a secure boot */
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index 287392bbdc..1742cf6f6c 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -27,7 +27,6 @@
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
-#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "hw/char/pl011.h"
diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c
index a8ec2cdf36..f68df56b97 100644
--- a/hw/arm/msf2-soc.c
+++ b/hw/arm/msf2-soc.c
@@ -29,7 +29,6 @@
#include "exec/address-spaces.h"
#include "hw/char/serial.h"
#include "hw/boards.h"
-#include "sysemu/block-backend.h"
#include "qemu/cutils.h"
#include "hw/arm/msf2-soc.h"
#include "hw/misc/unimp.h"
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index a37881433c..06f1e08ca9 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -27,12 +27,13 @@
#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */
#define FIRMWARE_ADDR_2 0x8000 /* Pi 2 loads kernel.img here by default */
#define FIRMWARE_ADDR_3 0x80000 /* Pi 3 loads kernel.img here by default */
+#define SPINTABLE_ADDR 0xd8 /* Pi 3 bootloader spintable */
/* Table of Linux board IDs for different Pi versions */
static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43, [3] = 0xc44};
typedef struct RasPiState {
- BCM2836State soc;
+ BCM283XState soc;
MemoryRegion ram;
} RasPiState;
@@ -63,6 +64,40 @@ static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info)
info->smp_loader_start);
}
+static void write_smpboot64(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+ /* Unlike the AArch32 version we don't need to call the board setup hook.
+ * The mechanism for doing the spin-table is also entirely different.
+ * We must have four 64-bit fields at absolute addresses
+ * 0xd8, 0xe0, 0xe8, 0xf0 in RAM, which are the flag variables for
+ * our CPUs, and which we must ensure are zero initialized before
+ * the primary CPU goes into the kernel. We put these variables inside
+ * a rom blob, so that the reset for ROM contents zeroes them for us.
+ */
+ static const uint32_t smpboot[] = {
+ 0xd2801b05, /* mov x5, 0xd8 */
+ 0xd53800a6, /* mrs x6, mpidr_el1 */
+ 0x924004c6, /* and x6, x6, #0x3 */
+ 0xd503205f, /* spin: wfe */
+ 0xf86678a4, /* ldr x4, [x5,x6,lsl #3] */
+ 0xb4ffffc4, /* cbz x4, spin */
+ 0xd2800000, /* mov x0, #0x0 */
+ 0xd2800001, /* mov x1, #0x0 */
+ 0xd2800002, /* mov x2, #0x0 */
+ 0xd2800003, /* mov x3, #0x0 */
+ 0xd61f0080, /* br x4 */
+ };
+
+ static const uint64_t spintables[] = {
+ 0, 0, 0, 0
+ };
+
+ rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+ rom_add_blob_fixed("raspi_spintables", spintables, sizeof(spintables),
+ SPINTABLE_ADDR);
+}
+
static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info)
{
arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR);
@@ -82,15 +117,28 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
binfo.board_id = raspi_boardid[version];
binfo.ram_size = ram_size;
binfo.nb_cpus = smp_cpus;
- binfo.board_setup_addr = BOARDSETUP_ADDR;
- binfo.write_board_setup = write_board_setup;
- binfo.secure_board_setup = true;
- binfo.secure_boot = true;
+
+ if (version <= 2) {
+ /* The rpi1 and 2 require some custom setup code to run in Secure
+ * mode before booting a kernel (to set up the SMC vectors so
+ * that we get a no-op SMC; this is used by Linux to call the
+ * firmware for some cache maintenance operations.
+ * The rpi3 doesn't need this.
+ */
+ binfo.board_setup_addr = BOARDSETUP_ADDR;
+ binfo.write_board_setup = write_board_setup;
+ binfo.secure_board_setup = true;
+ binfo.secure_boot = true;
+ }
/* Pi2 and Pi3 requires SMP setup */
if (version >= 2) {
binfo.smp_loader_start = SMPBOOT_ADDR;
- binfo.write_secondary_boot = write_smpboot;
+ if (version == 2) {
+ binfo.write_secondary_boot = write_smpboot;
+ } else {
+ binfo.write_secondary_boot = write_smpboot64;
+ }
binfo.secondary_cpu_reset_hook = reset_secondary;
}
@@ -127,7 +175,8 @@ static void raspi_init(MachineState *machine, int version)
BusState *bus;
DeviceState *carddev;
- object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
+ object_initialize(&s->soc, sizeof(s->soc),
+ version == 3 ? TYPE_BCM2837 : TYPE_BCM2836);
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
&error_abort);
@@ -140,8 +189,6 @@ static void raspi_init(MachineState *machine, int version)
/* Setup the SOC */
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
&error_abort);
- object_property_set_str(OBJECT(&s->soc), machine->cpu_type, "cpu-type",
- &error_abort);
object_property_set_int(OBJECT(&s->soc), smp_cpus, "enabled-cpus",
&error_abort);
int board_rev = version == 3 ? 0xa02082 : 0xa21041;
@@ -180,9 +227,9 @@ static void raspi2_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
mc->ignore_memory_transaction_failures = true;
};
@@ -203,9 +250,9 @@ static void raspi3_machine_init(MachineClass *mc)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53");
- mc->max_cpus = BCM2836_NCPUS;
- mc->min_cpus = BCM2836_NCPUS;
- mc->default_cpus = BCM2836_NCPUS;
+ mc->max_cpus = BCM283X_NCPUS;
+ mc->min_cpus = BCM283X_NCPUS;
+ mc->default_cpus = BCM283X_NCPUS;
mc->default_ram_size = 1024 * 1024 * 1024;
}
DEFINE_MACHINE("raspi3", raspi3_machine_init)
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 87cd1e583c..2139a62e25 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -20,7 +20,6 @@
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
-#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#include "hw/char/pl011.h"
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index a55b1a369c..7a925fa5e6 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -22,7 +22,6 @@
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
#include "hw/ssi/ssi.h"
-#include "sysemu/block-backend.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index 1bcd37e98d..1b979100b7 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -1,6 +1,7 @@
common-obj-$(CONFIG_IPACK) += ipoctal232.o
common-obj-$(CONFIG_ESCC) += escc.o
common-obj-$(CONFIG_PARALLEL) += parallel.o
+common-obj-$(CONFIG_PARALLEL) += parallel-isa.o
common-obj-$(CONFIG_PL011) += pl011.o
common-obj-$(CONFIG_SERIAL) += serial.o
common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index 70405ccf8b..1e5540472b 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -37,8 +37,8 @@
static const VMStateDescription vmstate_imx_serial = {
.name = TYPE_IMX_SERIAL,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_INT32(readbuff, IMXSerialState),
VMSTATE_UINT32(usr1, IMXSerialState),
@@ -50,22 +50,36 @@ static const VMStateDescription vmstate_imx_serial = {
VMSTATE_UINT32(ubmr, IMXSerialState),
VMSTATE_UINT32(ubrc, IMXSerialState),
VMSTATE_UINT32(ucr3, IMXSerialState),
+ VMSTATE_UINT32(ucr4, IMXSerialState),
VMSTATE_END_OF_LIST()
},
};
static void imx_update(IMXSerialState *s)
{
- uint32_t flags;
+ uint32_t usr1;
+ uint32_t usr2;
+ uint32_t mask;
- flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
- if (s->ucr1 & UCR1_TXMPTYEN) {
- flags |= (s->uts1 & UTS1_TXEMPTY);
- } else {
- flags &= ~USR1_TRDY;
- }
+ /*
+ * Lucky for us TRDY and RRDY has the same offset in both USR1 and
+ * UCR1, so we can get away with something as simple as the
+ * following:
+ */
+ usr1 = s->usr1 & s->ucr1 & (USR1_TRDY | USR1_RRDY);
+ /*
+ * Bits that we want in USR2 are not as conveniently laid out,
+ * unfortunately.
+ */
+ mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0;
+ /*
+ * TCEN and TXDC are both bit 3
+ */
+ mask |= s->ucr4 & UCR4_TCEN;
- qemu_set_irq(s->irq, !!flags);
+ usr2 = s->usr2 & mask;
+
+ qemu_set_irq(s->irq, usr1 || usr2);
}
static void imx_serial_reset(IMXSerialState *s)
@@ -155,6 +169,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
return s->ucr3;
case 0x23: /* UCR4 */
+ return s->ucr4;
+
case 0x29: /* BRM Incremental */
return 0x0; /* TODO */
@@ -183,8 +199,10 @@ static void imx_serial_write(void *opaque, hwaddr offset,
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&s->chr, &ch, 1);
s->usr1 &= ~USR1_TRDY;
+ s->usr2 &= ~USR2_TXDC;
imx_update(s);
s->usr1 |= USR1_TRDY;
+ s->usr2 |= USR2_TXDC;
imx_update(s);
}
break;
@@ -257,8 +275,12 @@ static void imx_serial_write(void *opaque, hwaddr offset,
s->ucr3 = value & 0xffff;
break;
- case 0x2d: /* UTS1 */
case 0x23: /* UCR4 */
+ s->ucr4 = value & 0xffff;
+ imx_update(s);
+ break;
+
+ case 0x2d: /* UTS1 */
qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
/* TODO */
diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c
new file mode 100644
index 0000000000..639e179585
--- /dev/null
+++ b/hw/char/parallel-isa.c
@@ -0,0 +1,36 @@
+/*
+ * QEMU Parallel PORT (ISA bus helpers)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "hw/isa/isa.h"
+#include "hw/char/parallel.h"
+
+static void parallel_init(ISABus *bus, int index, Chardev *chr)
+{
+ DeviceState *dev;
+ ISADevice *isadev;
+
+ isadev = isa_create(bus, "isa-parallel");
+ dev = DEVICE(isadev);
+ qdev_prop_set_uint32(dev, "index", index);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+}
+
+void parallel_hds_isa_init(ISABus *bus, int n)
+{
+ int i;
+
+ assert(n <= MAX_PARALLEL_PORTS);
+
+ for (i = 0; i < n; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(bus, i, parallel_hds[i]);
+ }
+ }
+}
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index f79dc76543..1542d62201 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -28,7 +28,7 @@
#include "chardev/char-parallel.h"
#include "chardev/char-fe.h"
#include "hw/isa/isa.h"
-#include "hw/i386/pc.h"
+#include "hw/char/parallel.h"
#include "sysemu/sysemu.h"
//#define DEBUG_PARALLEL
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 5e2bbcdace..2040177664 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -334,6 +334,22 @@ static bool machine_get_enforce_config_section(Object *obj, Error **errp)
return ms->enforce_config_section;
}
+static char *machine_get_memory_encryption(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return g_strdup(ms->memory_encryption);
+}
+
+static void machine_set_memory_encryption(Object *obj, const char *value,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ g_free(ms->memory_encryption);
+ ms->memory_encryption = g_strdup(value);
+}
+
void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type)
{
strList *item = g_new0(strList, 1);
@@ -612,6 +628,12 @@ static void machine_class_init(ObjectClass *oc, void *data)
&error_abort);
object_class_property_set_description(oc, "enforce-config-section",
"Set on to enforce configuration section migration", &error_abort);
+
+ object_class_property_add_str(oc, "memory-encryption",
+ machine_get_memory_encryption, machine_set_memory_encryption,
+ &error_abort);
+ object_class_property_set_description(oc, "memory-encryption",
+ "Set memory encyption object to use", &error_abort);
}
static void machine_class_base_init(ObjectClass *oc, void *data)
diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c
index 864832db34..cde4d3eb57 100644
--- a/hw/core/null-machine.c
+++ b/hw/core/null-machine.c
@@ -24,9 +24,9 @@ static void machine_none_init(MachineState *mch)
{
CPUState *cpu = NULL;
- /* Initialize CPU (if a model has been specified) */
- if (mch->cpu_model) {
- cpu = cpu_init(mch->cpu_model);
+ /* Initialize CPU (if user asked for it) */
+ if (mch->cpu_type) {
+ cpu = cpu_create(mch->cpu_type);
if (!cpu) {
error_report("Unable to initialize CPU");
exit(1);
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 1674bd3581..f312930664 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -292,6 +292,14 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
}
+static void pci_secondary_vga_exit(PCIDevice *dev)
+{
+ PCIVGAState *d = PCI_VGA(dev);
+ VGACommonState *s = &d->vga;
+
+ graphic_console_close(s->con);
+}
+
static void pci_secondary_vga_init(Object *obj)
{
/* Expose framebuffer byteorder via QOM */
@@ -361,6 +369,7 @@ static void secondary_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = pci_secondary_vga_realize;
+ k->exit = pci_secondary_vga_exit;
k->class_id = PCI_CLASS_DISPLAY_OTHER;
dc->props = secondary_pci_properties;
dc->reset = pci_secondary_vga_reset;
diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c
index 6c0f975df0..83c87d92e0 100644
--- a/hw/dma/i82374.c
+++ b/hw/dma/i82374.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "hw/isa/isa.h"
+#include "hw/dma/i8257.h"
#define TYPE_I82374 "i82374"
#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374)
@@ -123,7 +124,7 @@ static void i82374_realize(DeviceState *dev, Error **errp)
portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj),
s->iobase);
- DMA_init(isa_bus_from_device(ISA_DEVICE(dev)), 1);
+ i8257_dma_init(isa_bus_from_device(ISA_DEVICE(dev)), true);
memset(s->commands, 0, sizeof(s->commands));
}
diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c
index bd23e893bf..52675e97c9 100644
--- a/hw/dma/i8257.c
+++ b/hw/dma/i8257.c
@@ -24,7 +24,7 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
-#include "hw/isa/i8257.h"
+#include "hw/dma/i8257.h"
#include "qemu/main-loop.h"
#include "trace.h"
@@ -622,7 +622,7 @@ static void i8257_register_types(void)
type_init(i8257_register_types)
-void DMA_init(ISABus *bus, int high_page_enable)
+void i8257_dma_init(ISABus *bus, bool high_page_enable)
{
ISADevice *isa1, *isa2;
DeviceState *d;
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index fd279e7584..fa87a14152 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -2,8 +2,8 @@ obj-$(CONFIG_KVM) += kvm/
obj-y += multiboot.o
obj-y += pc.o pc_piix.o pc_q35.o
obj-y += pc_sysfw.o
-obj-y += x86-iommu.o intel_iommu.o
-obj-y += amd_iommu.o
+obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o
+obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o
obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-$(CONFIG_VMPORT) += vmport.o
obj-$(CONFIG_VMMOUSE) += vmmouse.o
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 1df9ed275e..3cf2a1679c 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -194,21 +194,21 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
/* Fill in optional s3/s4 related properties */
o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL);
if (o) {
- pm->s3_disabled = qnum_get_uint(qobject_to_qnum(o));
+ pm->s3_disabled = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s3_disabled = false;
}
qobject_decref(o);
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL);
if (o) {
- pm->s4_disabled = qnum_get_uint(qobject_to_qnum(o));
+ pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s4_disabled = false;
}
qobject_decref(o);
o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL);
if (o) {
- pm->s4_val = qnum_get_uint(qobject_to_qnum(o));
+ pm->s4_val = qnum_get_uint(qobject_to(QNum, o));
} else {
pm->s4_val = false;
}
@@ -429,14 +429,14 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
bool pcihp_bridge_en)
{
- Aml *dev, *notify_method, *method;
+ Aml *dev, *notify_method = NULL, *method;
QObject *bsel;
PCIBus *sec;
int i;
bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
if (bsel) {
- uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
@@ -546,7 +546,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
/* If bus supports hotplug select it and notify about local events */
if (bsel) {
- uint64_t bsel_val = qnum_get_uint(qobject_to_qnum(bsel));
+ uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel));
aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM")));
aml_append(method,
@@ -2613,12 +2613,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
if (!o) {
return false;
}
- mcfg->mcfg_base = qnum_get_uint(qobject_to_qnum(o));
+ mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o));
qobject_decref(o);
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL);
assert(o);
- mcfg->mcfg_size = qnum_get_uint(qobject_to_qnum(o));
+ mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o));
qobject_decref(o);
return true;
}
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 35fcb6efdf..d36bac8c89 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -26,6 +26,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
+#include "hw/char/parallel.h"
#include "hw/i386/apic.h"
#include "hw/i386/topology.h"
#include "sysemu/cpus.h"
@@ -40,7 +41,9 @@
#include "elf.h"
#include "multiboot.h"
#include "hw/timer/mc146818rtc.h"
+#include "hw/dma/i8257.h"
#include "hw/timer/i8254.h"
+#include "hw/input/i8042.h"
#include "hw/audio/pcspk.h"
#include "hw/pci/msi.h"
#include "hw/sysbus.h"
@@ -50,8 +53,6 @@
#include "sysemu/qtest.h"
#include "kvm_i386.h"
#include "hw/xen/xen.h"
-#include "sysemu/block-backend.h"
-#include "hw/block/block.h"
#include "ui/qemu-spice.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
@@ -1516,6 +1517,44 @@ static const MemoryRegionOps ioportF0_io_ops = {
},
};
+static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
+{
+ int i;
+ DriveInfo *fd[MAX_FD];
+ qemu_irq *a20_line;
+ ISADevice *i8042, *port92, *vmmouse;
+
+ serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
+ parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
+
+ for (i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ create_fdctrl |= !!fd[i];
+ }
+ if (create_fdctrl) {
+ fdctrl_init_isa(isa_bus, fd);
+ }
+
+ i8042 = isa_create_simple(isa_bus, "i8042");
+ if (!no_vmport) {
+ vmport_init(isa_bus);
+ vmmouse = isa_try_create(isa_bus, "vmmouse");
+ } else {
+ vmmouse = NULL;
+ }
+ if (vmmouse) {
+ DeviceState *dev = DEVICE(vmmouse);
+ qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
+ qdev_init_nofail(dev);
+ }
+ port92 = isa_create_simple(isa_bus, "port92");
+
+ a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
+ i8042_setup_a20_line(i8042, a20_line[0]);
+ port92_init(port92, a20_line[1]);
+ g_free(a20_line);
+}
+
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
bool create_fdctrl,
@@ -1524,13 +1563,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
uint32_t hpet_irqs)
{
int i;
- DriveInfo *fd[MAX_FD];
DeviceState *hpet = NULL;
int pit_isa_irq = 0;
qemu_irq pit_alt_irq = NULL;
qemu_irq rtc_irq = NULL;
- qemu_irq *a20_line;
- ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
+ ISADevice *pit = NULL;
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
@@ -1587,50 +1624,25 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
pcspk_init(isa_bus, pit);
}
- serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
- parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
-
- a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
- i8042 = isa_create_simple(isa_bus, "i8042");
- i8042_setup_a20_line(i8042, a20_line[0]);
- if (!no_vmport) {
- vmport_init(isa_bus);
- vmmouse = isa_try_create(isa_bus, "vmmouse");
- } else {
- vmmouse = NULL;
- }
- if (vmmouse) {
- DeviceState *dev = DEVICE(vmmouse);
- qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
- qdev_init_nofail(dev);
- }
- port92 = isa_create_simple(isa_bus, "port92");
- port92_init(port92, a20_line[1]);
- g_free(a20_line);
-
- DMA_init(isa_bus, 0);
+ i8257_dma_init(isa_bus, 0);
- for(i = 0; i < MAX_FD; i++) {
- fd[i] = drive_get(IF_FLOPPY, 0, i);
- create_fdctrl |= !!fd[i];
- }
- if (create_fdctrl) {
- fdctrl_init_isa(isa_bus, fd);
- }
+ /* Super I/O */
+ pc_superio_init(isa_bus, create_fdctrl, no_vmport);
}
-void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
+void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
{
int i;
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
+ const char *model = nd->model ? nd->model : pcmc->default_nic_model;
- if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
+ if (g_str_equal(model, "ne2k_isa")) {
pc_init_ne2k_isa(isa_bus, nd);
} else {
- pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
+ pci_nic_init_nofail(nd, pci_bus, model, NULL);
}
}
rom_reset_order_override();
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 8658bcba63..729a0508aa 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -40,7 +40,6 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "sysemu/arch_init.h"
-#include "sysemu/block-backend.h"
#include "hw/i2c/smbus.h"
#include "hw/xen/xen.h"
#include "exec/memory.h"
@@ -240,7 +239,7 @@ static void pc_init1(MachineState *machine,
pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true,
(pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, 0x4);
- pc_nic_init(isa_bus, pci_bus);
+ pc_nic_init(pcmc, isa_bus, pci_bus);
ide_drive_get(hd, ARRAY_SIZE(hd));
if (pcmc->pci_enabled) {
@@ -417,6 +416,9 @@ static void pc_xen_hvm_init(MachineState *machine)
static void pc_i440fx_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pcmc->default_nic_model = "e1000";
+
m->family = "pc_piix";
m->desc = "Standard PC (i440FX + PIIX, 1996)";
m->default_machine_opts = "firmware=bios-256k.bin";
@@ -1114,6 +1116,7 @@ static void isapc_machine_options(MachineClass *m)
pcmc->gigabyte_align = false;
pcmc->smbios_legacy_mode = true;
pcmc->has_reserved_memory = false;
+ pcmc->default_nic_model = "ne2k_isa";
m->default_cpu_type = X86_CPU_TYPE_NAME("486");
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 0c0bc48137..9ae916327e 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -272,7 +272,7 @@ static void pc_q35_init(MachineState *machine)
/* the rest devices to which pci devfn is automatically assigned */
pc_vga_init(isa_bus, host_bus);
- pc_nic_init(isa_bus, host_bus);
+ pc_nic_init(pcmc, isa_bus, host_bus);
if (pcms->acpi_nvdimm_state.is_enabled) {
nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
@@ -294,6 +294,9 @@ static void pc_q35_init(MachineState *machine)
static void pc_q35_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pcmc->default_nic_model = "e1000e";
+
m->family = "pc_q35";
m->desc = "Standard PC (Q35 + ICH9, 2009)";
m->units_per_default_bus = 1;
@@ -316,7 +319,10 @@ DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL,
static void pc_q35_2_11_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+
pc_q35_2_12_machine_options(m);
+ pcmc->default_nic_model = "e1000";
m->alias = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_11);
}
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 4325575e7d..73ac783f20 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -113,6 +113,8 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
pflash_t *system_flash;
MemoryRegion *flash_mem;
char name[64];
+ void *flash_ptr;
+ int ret, flash_size;
sector_bits = 12;
sector_size = 1 << sector_bits;
@@ -169,6 +171,17 @@ static void pc_system_flash_init(MemoryRegion *rom_memory)
if (unit == 0) {
flash_mem = pflash_cfi01_get_memory(system_flash);
pc_isa_bios_init(rom_memory, flash_mem, size);
+
+ /* Encrypt the pflash boot ROM */
+ if (kvm_memcrypt_enabled()) {
+ flash_ptr = memory_region_get_ram_ptr(flash_mem);
+ flash_size = memory_region_size(flash_mem);
+ ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size);
+ if (ret) {
+ error_report("failed to encrypt pflash rom");
+ exit(1);
+ }
+ }
}
}
}
diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c
index 65ef55329e..5d2d278be4 100644
--- a/hw/i386/vmmouse.c
+++ b/hw/i386/vmmouse.c
@@ -25,6 +25,7 @@
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/i386/pc.h"
+#include "hw/input/i8042.h"
#include "hw/qdev.h"
/* debug only vmmouse */
diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c
index 116aa09819..3bf8cfe041 100644
--- a/hw/i386/vmport.c
+++ b/hw/i386/vmport.c
@@ -25,6 +25,7 @@
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
+#include "hw/input/i8042.h"
#include "sysemu/hw_accel.h"
#include "hw/qdev.h"
#include "qemu/log.h"
diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c
index c3f1604936..5397483fd8 100644
--- a/hw/ide/ahci-allwinner.c
+++ b/hw/ide/ahci-allwinner.c
@@ -18,7 +18,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/error-report.h"
-#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
#include "hw/ide/ahci_internal.h"
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index 65aff518ec..6bb92d717f 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -26,7 +26,6 @@
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
-#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "sysemu/dma.h"
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index c01b24ecbe..134478ebb2 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -65,7 +65,6 @@
#include "hw/pci/msi.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
-#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci_internal.h"
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
index 9fb24fc92b..028bd61774 100644
--- a/hw/ide/isa.c
+++ b/hw/ide/isa.c
@@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/isa/isa.h"
-#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c
index 58e4f52f7d..34bb98dce8 100644
--- a/hw/ide/microdrive.c
+++ b/hw/ide/microdrive.c
@@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/pcmcia.h"
-#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c
index 6f12f456ed..42fcf139ee 100644
--- a/hw/ide/mmio.c
+++ b/hw/ide/mmio.c
@@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
-#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "hw/ide/internal.h"
diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c
index e3896c65b4..743a50ed51 100644
--- a/hw/ide/sii3112.c
+++ b/hw/ide/sii3112.c
@@ -327,17 +327,6 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp)
qemu_register_reset(sii3112_reset, s);
}
-static void sii3112_pci_exitfn(PCIDevice *dev)
-{
- PCIIDEState *d = PCI_IDE(dev);
- int i;
-
- for (i = 0; i < 2; ++i) {
- memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io);
- memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport);
- }
-}
-
static void sii3112_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -348,7 +337,6 @@ static void sii3112_pci_class_init(ObjectClass *klass, void *data)
pd->class_id = PCI_CLASS_STORAGE_RAID;
pd->revision = 1;
pd->realize = sii3112_pci_realize;
- pd->exit = sii3112_pci_exitfn;
dc->desc = "SiI3112A SATA controller";
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index c479f827b6..f17f18e51b 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -26,6 +26,7 @@
#include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#include "hw/input/ps2.h"
+#include "hw/input/i8042.h"
#include "sysemu/sysemu.h"
/* debug PC keyboard */
@@ -480,7 +481,6 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
qemu_register_reset(kbd_reset, s);
}
-#define TYPE_I8042 "i8042"
#define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042)
typedef struct ISAKBDState {
diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs
index fb37c55cf2..83e06f6c04 100644
--- a/hw/isa/Makefile.objs
+++ b/hw/isa/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-$(CONFIG_ISA_BUS) += isa-bus.o
+common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o
common-obj-$(CONFIG_APM) += apm.o
common-obj-$(CONFIG_I82378) += i82378.o
common-obj-$(CONFIG_PC87312) += pc87312.o
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 0f2e426d02..63fa77effc 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -24,7 +24,6 @@
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
-#include "hw/i386/pc.h"
static ISABus *isabus;
@@ -288,28 +287,3 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
}
type_init(isabus_register_types)
-
-static void parallel_init(ISABus *bus, int index, Chardev *chr)
-{
- DeviceState *dev;
- ISADevice *isadev;
-
- isadev = isa_create(bus, "isa-parallel");
- dev = DEVICE(isadev);
- qdev_prop_set_uint32(dev, "index", index);
- qdev_prop_set_chr(dev, "chardev", chr);
- qdev_init_nofail(dev);
-}
-
-void parallel_hds_isa_init(ISABus *bus, int n)
-{
- int i;
-
- assert(n <= MAX_PARALLEL_PORTS);
-
- for (i = 0; i < n; i++) {
- if (parallel_hds[i]) {
- parallel_init(bus, i, parallel_hds[i]);
- }
- }
-}
diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c
new file mode 100644
index 0000000000..b95608a003
--- /dev/null
+++ b/hw/isa/isa-superio.c
@@ -0,0 +1,214 @@
+/*
+ * Generic ISA Super I/O
+ *
+ * Copyright (c) 2010-2012 Herve Poussineau
+ * Copyright (c) 2011-2012 Andreas Färber
+ * Copyright (c) 2018 Philippe Mathieu-Daudé
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/blockdev.h"
+#include "chardev/char.h"
+#include "hw/isa/superio.h"
+#include "hw/input/i8042.h"
+#include "hw/char/serial.h"
+#include "trace.h"
+
+static void isa_superio_realize(DeviceState *dev, Error **errp)
+{
+ ISASuperIODevice *sio = ISA_SUPERIO(dev);
+ ISASuperIOClass *k = ISA_SUPERIO_GET_CLASS(sio);
+ ISABus *bus = isa_bus_from_device(ISA_DEVICE(dev));
+ ISADevice *isa;
+ DeviceState *d;
+ Chardev *chr;
+ DriveInfo *drive;
+ char *name;
+ int i;
+
+ /* Parallel port */
+ for (i = 0; i < k->parallel.count; i++) {
+ if (i >= ARRAY_SIZE(sio->parallel)) {
+ warn_report("superio: ignoring %td parallel controllers",
+ k->parallel.count - ARRAY_SIZE(sio->parallel));
+ break;
+ }
+ if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) {
+ /* FIXME use a qdev chardev prop instead of parallel_hds[] */
+ chr = parallel_hds[i];
+ if (chr == NULL || chr->be) {
+ name = g_strdup_printf("discarding-parallel%d", i);
+ chr = qemu_chr_new(name, "null");
+ } else {
+ name = g_strdup_printf("parallel%d", i);
+ }
+ isa = isa_create(bus, "isa-parallel");
+ d = DEVICE(isa);
+ qdev_prop_set_uint32(d, "index", i);
+ if (k->parallel.get_iobase) {
+ qdev_prop_set_uint32(d, "iobase",
+ k->parallel.get_iobase(sio, i));
+ }
+ if (k->parallel.get_irq) {
+ qdev_prop_set_uint32(d, "irq", k->parallel.get_irq(sio, i));
+ }
+ qdev_prop_set_chr(d, "chardev", chr);
+ qdev_init_nofail(d);
+ sio->parallel[i] = isa;
+ trace_superio_create_parallel(i,
+ k->parallel.get_iobase ?
+ k->parallel.get_iobase(sio, i) : -1,
+ k->parallel.get_irq ?
+ k->parallel.get_irq(sio, i) : -1);
+ object_property_add_child(OBJECT(dev), name,
+ OBJECT(sio->parallel[i]), NULL);
+ g_free(name);
+ }
+ }
+
+ /* Serial */
+ for (i = 0; i < k->serial.count; i++) {
+ if (i >= ARRAY_SIZE(sio->serial)) {
+ warn_report("superio: ignoring %td serial controllers",
+ k->serial.count - ARRAY_SIZE(sio->serial));
+ break;
+ }
+ if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) {
+ /* FIXME use a qdev chardev prop instead of serial_hds[] */
+ chr = serial_hds[i];
+ if (chr == NULL || chr->be) {
+ name = g_strdup_printf("discarding-serial%d", i);
+ chr = qemu_chr_new(name, "null");
+ } else {
+ name = g_strdup_printf("serial%d", i);
+ }
+ isa = isa_create(bus, TYPE_ISA_SERIAL);
+ d = DEVICE(isa);
+ qdev_prop_set_uint32(d, "index", i);
+ if (k->serial.get_iobase) {
+ qdev_prop_set_uint32(d, "iobase",
+ k->serial.get_iobase(sio, i));
+ }
+ if (k->serial.get_irq) {
+ qdev_prop_set_uint32(d, "irq", k->serial.get_irq(sio, i));
+ }
+ qdev_prop_set_chr(d, "chardev", chr);
+ qdev_init_nofail(d);
+ sio->serial[i] = isa;
+ trace_superio_create_serial(i,
+ k->serial.get_iobase ?
+ k->serial.get_iobase(sio, i) : -1,
+ k->serial.get_irq ?
+ k->serial.get_irq(sio, i) : -1);
+ object_property_add_child(OBJECT(dev), name,
+ OBJECT(sio->serial[0]), NULL);
+ g_free(name);
+ }
+ }
+
+ /* Floppy disc */
+ if (!k->floppy.is_enabled || k->floppy.is_enabled(sio, 0)) {
+ isa = isa_create(bus, "isa-fdc");
+ d = DEVICE(isa);
+ if (k->floppy.get_iobase) {
+ qdev_prop_set_uint32(d, "iobase", k->floppy.get_iobase(sio, 0));
+ }
+ if (k->floppy.get_irq) {
+ qdev_prop_set_uint32(d, "irq", k->floppy.get_irq(sio, 0));
+ }
+ /* FIXME use a qdev drive property instead of drive_get() */
+ drive = drive_get(IF_FLOPPY, 0, 0);
+ if (drive != NULL) {
+ qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
+ &error_fatal);
+ }
+ /* FIXME use a qdev drive property instead of drive_get() */
+ drive = drive_get(IF_FLOPPY, 0, 1);
+ if (drive != NULL) {
+ qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
+ &error_fatal);
+ }
+ qdev_init_nofail(d);
+ sio->floppy = isa;
+ trace_superio_create_floppy(0,
+ k->floppy.get_iobase ?
+ k->floppy.get_iobase(sio, 0) : -1,
+ k->floppy.get_irq ?
+ k->floppy.get_irq(sio, 0) : -1);
+ }
+
+ /* Keyboard, mouse */
+ sio->kbc = isa_create_simple(bus, TYPE_I8042);
+
+ /* IDE */
+ if (k->ide.count && (!k->ide.is_enabled || k->ide.is_enabled(sio, 0))) {
+ isa = isa_create(bus, "isa-ide");
+ d = DEVICE(isa);
+ if (k->ide.get_iobase) {
+ qdev_prop_set_uint32(d, "iobase", k->ide.get_iobase(sio, 0));
+ }
+ if (k->ide.get_iobase) {
+ qdev_prop_set_uint32(d, "iobase2", k->ide.get_iobase(sio, 1));
+ }
+ if (k->ide.get_irq) {
+ qdev_prop_set_uint32(d, "irq", k->ide.get_irq(sio, 0));
+ }
+ qdev_init_nofail(d);
+ sio->ide = isa;
+ trace_superio_create_ide(0,
+ k->ide.get_iobase ?
+ k->ide.get_iobase(sio, 0) : -1,
+ k->ide.get_irq ?
+ k->ide.get_irq(sio, 0) : -1);
+ }
+}
+
+static void isa_superio_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = isa_superio_realize;
+ /* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */
+ dc->user_creatable = false;
+}
+
+static const TypeInfo isa_superio_type_info = {
+ .name = TYPE_ISA_SUPERIO,
+ .parent = TYPE_ISA_DEVICE,
+ .abstract = true,
+ .class_size = sizeof(ISASuperIOClass),
+ .class_init = isa_superio_class_init,
+};
+
+/* SMS FDC37M817 Super I/O */
+static void fdc37m81x_class_init(ObjectClass *klass, void *data)
+{
+ ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
+
+ sc->serial.count = 2; /* NS16C550A */
+ sc->parallel.count = 1;
+ sc->floppy.count = 1; /* SMSC 82077AA Compatible */
+ sc->ide.count = 0;
+}
+
+static const TypeInfo fdc37m81x_type_info = {
+ .name = TYPE_FDC37M81X_SUPERIO,
+ .parent = TYPE_ISA_SUPERIO,
+ .instance_size = sizeof(ISASuperIODevice),
+ .class_init = fdc37m81x_class_init,
+};
+
+static void isa_superio_register_types(void)
+{
+ type_register_static(&isa_superio_type_info);
+ type_register_static(&fdc37m81x_type_info);
+}
+
+type_init(isa_superio_register_types)
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
index 48b29e3c3c..5cf64505fe 100644
--- a/hw/isa/pc87312.c
+++ b/hw/isa/pc87312.c
@@ -27,10 +27,6 @@
#include "hw/isa/pc87312.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
-#include "sysemu/block-backend.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/sysemu.h"
-#include "chardev/char.h"
#include "trace.h"
@@ -64,22 +60,25 @@
/* Parallel port */
-static inline bool is_parallel_enabled(PC87312State *s)
+static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
{
- return s->regs[REG_FER] & FER_PARALLEL_EN;
+ PC87312State *s = PC87312(sio);
+ return index ? false : s->regs[REG_FER] & FER_PARALLEL_EN;
}
-static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
+static const uint16_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
-static inline uint32_t get_parallel_iobase(PC87312State *s)
+static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
}
-static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
+static const unsigned int parallel_irq[] = { 5, 7, 5, 0 };
-static inline uint32_t get_parallel_irq(PC87312State *s)
+static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
if (idx == 0) {
@@ -92,13 +91,14 @@ static inline uint32_t get_parallel_irq(PC87312State *s)
/* UARTs */
-static const uint32_t uart_base[2][4] = {
+static const uint16_t uart_base[2][4] = {
{ 0x3e8, 0x338, 0x2e8, 0x220 },
{ 0x2e8, 0x238, 0x2e0, 0x228 }
};
-static inline uint32_t get_uart_iobase(PC87312State *s, int i)
+static uint16_t get_uart_iobase(ISASuperIODevice *sio, uint8_t i)
{
+ PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
if (idx == 0) {
@@ -110,44 +110,68 @@ static inline uint32_t get_uart_iobase(PC87312State *s, int i)
}
}
-static inline uint32_t get_uart_irq(PC87312State *s, int i)
+static unsigned int get_uart_irq(ISASuperIODevice *sio, uint8_t i)
{
+ PC87312State *s = PC87312(sio);
int idx;
idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
return (idx & 1) ? 3 : 4;
}
-static inline bool is_uart_enabled(PC87312State *s, int i)
+static bool is_uart_enabled(ISASuperIODevice *sio, uint8_t i)
{
+ PC87312State *s = PC87312(sio);
return s->regs[REG_FER] & (FER_UART1_EN << i);
}
/* Floppy controller */
-static inline bool is_fdc_enabled(PC87312State *s)
+static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
+ assert(!index);
return s->regs[REG_FER] & FER_FDC_EN;
}
-static inline uint32_t get_fdc_iobase(PC87312State *s)
+static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
+ assert(!index);
return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
}
+static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
+{
+ assert(!index);
+ return 6;
+}
+
/* IDE controller */
-static inline bool is_ide_enabled(PC87312State *s)
+static bool is_ide_enabled(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
+
return s->regs[REG_FER] & FER_IDE_EN;
}
-static inline uint32_t get_ide_iobase(PC87312State *s)
+static uint16_t get_ide_iobase(ISASuperIODevice *sio, uint8_t index)
{
+ PC87312State *s = PC87312(sio);
+
+ if (index == 1) {
+ return get_ide_iobase(sio, 0) + 0x206;
+ }
return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
}
+static unsigned int get_ide_irq(ISASuperIODevice *sio, uint8_t index)
+{
+ assert(index == 0);
+ return 14;
+}
static void reconfigure_devices(PC87312State *s)
{
@@ -265,90 +289,18 @@ static void pc87312_reset(DeviceState *d)
static void pc87312_realize(DeviceState *dev, Error **errp)
{
PC87312State *s;
- DeviceState *d;
ISADevice *isa;
- ISABus *bus;
- Chardev *chr;
- DriveInfo *drive;
- char name[5];
- int i;
+ Error *local_err = NULL;
s = PC87312(dev);
isa = ISA_DEVICE(dev);
- bus = isa_bus_from_device(isa);
isa_register_ioport(isa, &s->io, s->iobase);
pc87312_hard_reset(s);
- if (is_parallel_enabled(s)) {
- /* FIXME use a qdev chardev prop instead of parallel_hds[] */
- chr = parallel_hds[0];
- if (chr == NULL) {
- chr = qemu_chr_new("par0", "null");
- }
- isa = isa_create(bus, "isa-parallel");
- d = DEVICE(isa);
- qdev_prop_set_uint32(d, "index", 0);
- qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
- qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
- qdev_prop_set_chr(d, "chardev", chr);
- qdev_init_nofail(d);
- s->parallel.dev = isa;
- trace_pc87312_info_parallel(get_parallel_iobase(s),
- get_parallel_irq(s));
- }
-
- for (i = 0; i < 2; i++) {
- if (is_uart_enabled(s, i)) {
- /* FIXME use a qdev chardev prop instead of serial_hds[] */
- chr = serial_hds[i];
- if (chr == NULL) {
- snprintf(name, sizeof(name), "ser%d", i);
- chr = qemu_chr_new(name, "null");
- }
- isa = isa_create(bus, "isa-serial");
- d = DEVICE(isa);
- qdev_prop_set_uint32(d, "index", i);
- qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
- qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
- qdev_prop_set_chr(d, "chardev", chr);
- qdev_init_nofail(d);
- s->uart[i].dev = isa;
- trace_pc87312_info_serial(i, get_uart_iobase(s, i),
- get_uart_irq(s, i));
- }
- }
-
- if (is_fdc_enabled(s)) {
- isa = isa_create(bus, "isa-fdc");
- d = DEVICE(isa);
- qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
- qdev_prop_set_uint32(d, "irq", 6);
- /* FIXME use a qdev drive property instead of drive_get() */
- drive = drive_get(IF_FLOPPY, 0, 0);
- if (drive != NULL) {
- qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
- &error_fatal);
- }
- /* FIXME use a qdev drive property instead of drive_get() */
- drive = drive_get(IF_FLOPPY, 0, 1);
- if (drive != NULL) {
- qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
- &error_fatal);
- }
- qdev_init_nofail(d);
- s->fdc.dev = isa;
- trace_pc87312_info_floppy(get_fdc_iobase(s));
- }
-
- if (is_ide_enabled(s)) {
- isa = isa_create(bus, "isa-ide");
- d = DEVICE(isa);
- qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
- qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
- qdev_prop_set_uint32(d, "irq", 14);
- qdev_init_nofail(d);
- s->ide.dev = isa;
- trace_pc87312_info_ide(get_ide_iobase(s));
+ ISA_SUPERIO_GET_CLASS(dev)->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
}
}
@@ -373,7 +325,7 @@ static const VMStateDescription vmstate_pc87312 = {
};
static Property pc87312_properties[] = {
- DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398),
+ DEFINE_PROP_UINT16("iobase", PC87312State, iobase, 0x398),
DEFINE_PROP_UINT8("config", PC87312State, config, 1),
DEFINE_PROP_END_OF_LIST()
};
@@ -381,21 +333,47 @@ static Property pc87312_properties[] = {
static void pc87312_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
+ sc->parent_realize = dc->realize;
dc->realize = pc87312_realize;
dc->reset = pc87312_reset;
dc->vmsd = &vmstate_pc87312;
dc->props = pc87312_properties;
- /* Reason: Uses parallel_hds[0] in realize(), so it can't be used twice */
- dc->user_creatable = false;
+
+ sc->parallel = (ISASuperIOFuncs){
+ .count = 1,
+ .is_enabled = is_parallel_enabled,
+ .get_iobase = get_parallel_iobase,
+ .get_irq = get_parallel_irq,
+ };
+ sc->serial = (ISASuperIOFuncs){
+ .count = 2,
+ .is_enabled = is_uart_enabled,
+ .get_iobase = get_uart_iobase,
+ .get_irq = get_uart_irq,
+ };
+ sc->floppy = (ISASuperIOFuncs){
+ .count = 1,
+ .is_enabled = is_fdc_enabled,
+ .get_iobase = get_fdc_iobase,
+ .get_irq = get_fdc_irq,
+ };
+ sc->ide = (ISASuperIOFuncs){
+ .count = 1,
+ .is_enabled = is_ide_enabled,
+ .get_iobase = get_ide_iobase,
+ .get_irq = get_ide_irq,
+ };
}
static const TypeInfo pc87312_type_info = {
- .name = TYPE_PC87312,
- .parent = TYPE_ISA_DEVICE,
+ .name = TYPE_PC87312_SUPERIO,
+ .parent = TYPE_ISA_SUPERIO,
.instance_size = sizeof(PC87312State),
.instance_init = pc87312_initfn,
.class_init = pc87312_class_init,
+ /* FIXME use a qdev drive property instead of drive_get() */
};
static void pc87312_register_types(void)
diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c
new file mode 100644
index 0000000000..aa233c6967
--- /dev/null
+++ b/hw/isa/smc37c669-superio.c
@@ -0,0 +1,115 @@
+/*
+ * SMC FDC37C669 Super I/O controller
+ *
+ * Copyright (c) 2018 Philippe Mathieu-Daudé
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/isa/superio.h"
+
+/* UARTs (compatible with NS16450 or PC16550) */
+
+static bool is_serial_enabled(ISASuperIODevice *sio, uint8_t index)
+{
+ return index < 2;
+}
+
+static uint16_t get_serial_iobase(ISASuperIODevice *sio, uint8_t index)
+{
+ return index ? 0x2f8 : 0x3f8;
+}
+
+static unsigned int get_serial_irq(ISASuperIODevice *sio, uint8_t index)
+{
+ return index ? 3 : 4;
+}
+
+/* Parallel port */
+
+static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index)
+{
+ return index < 1;
+}
+
+static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index)
+{
+ return 0x3bc;
+}
+
+static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index)
+{
+ return 7;
+}
+
+static unsigned int get_parallel_dma(ISASuperIODevice *sio, uint8_t index)
+{
+ return 3;
+}
+
+/* Diskette controller (Software compatible with the Intel PC8477) */
+
+static bool is_fdc_enabled(ISASuperIODevice *sio, uint8_t index)
+{
+ return index < 1;
+}
+
+static uint16_t get_fdc_iobase(ISASuperIODevice *sio, uint8_t index)
+{
+ return 0x3f0;
+}
+
+static unsigned int get_fdc_irq(ISASuperIODevice *sio, uint8_t index)
+{
+ return 6;
+}
+
+static unsigned int get_fdc_dma(ISASuperIODevice *sio, uint8_t index)
+{
+ return 2;
+}
+
+static void smc37c669_class_init(ObjectClass *klass, void *data)
+{
+ ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
+
+ sc->parallel = (ISASuperIOFuncs){
+ .count = 1,
+ .is_enabled = is_parallel_enabled,
+ .get_iobase = get_parallel_iobase,
+ .get_irq = get_parallel_irq,
+ .get_dma = get_parallel_dma,
+ };
+ sc->serial = (ISASuperIOFuncs){
+ .count = 2,
+ .is_enabled = is_serial_enabled,
+ .get_iobase = get_serial_iobase,
+ .get_irq = get_serial_irq,
+ };
+ sc->floppy = (ISASuperIOFuncs){
+ .count = 1,
+ .is_enabled = is_fdc_enabled,
+ .get_iobase = get_fdc_iobase,
+ .get_irq = get_fdc_irq,
+ .get_dma = get_fdc_dma,
+ };
+ sc->ide.count = 0;
+}
+
+static const TypeInfo smc37c669_type_info = {
+ .name = TYPE_SMC37C669_SUPERIO,
+ .parent = TYPE_ISA_SUPERIO,
+ .instance_size = sizeof(ISASuperIODevice),
+ .class_size = sizeof(ISASuperIOClass),
+ .class_init = smc37c669_class_init,
+};
+
+static void smc37c669_register_types(void)
+{
+ type_register_static(&smc37c669_type_info);
+}
+
+type_init(smc37c669_register_types)
diff --git a/hw/isa/trace-events b/hw/isa/trace-events
index a4ab4e3634..80ac6175d6 100644
--- a/hw/isa/trace-events
+++ b/hw/isa/trace-events
@@ -1,9 +1,11 @@
# See docs/devel/tracing.txt for syntax documentation.
+# hw/isa/isa-superio.c
+superio_create_parallel(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
+superio_create_serial(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
+superio_create_floppy(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
+superio_create_ide(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u"
+
# hw/isa/pc87312.c
pc87312_io_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x"
pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x"
-pc87312_info_floppy(uint32_t base) "base 0x%x"
-pc87312_info_ide(uint32_t base) "base 0x%x"
-pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u"
-pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u"
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index 070cc1889f..cff1946232 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -17,6 +17,7 @@
#include "hw/i2c/smbus.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
+#include "hw/isa/superio.h"
#include "hw/sysbus.h"
#include "hw/mips/mips.h"
#include "hw/isa/apm.h"
@@ -478,7 +479,7 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
qemu_register_reset(vt82c686b_reset, d);
}
-ISABus *vt82c686b_init(PCIBus *bus, int devfn)
+ISABus *vt82c686b_isa_init(PCIBus *bus, int devfn)
{
PCIDevice *d;
@@ -519,11 +520,30 @@ static const TypeInfo via_info = {
},
};
+static void vt82c686b_superio_class_init(ObjectClass *klass, void *data)
+{
+ ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
+
+ sc->serial.count = 2;
+ sc->parallel.count = 1;
+ sc->ide.count = 0;
+ sc->floppy.count = 1;
+}
+
+static const TypeInfo via_superio_info = {
+ .name = TYPE_VT82C686B_SUPERIO,
+ .parent = TYPE_ISA_SUPERIO,
+ .instance_size = sizeof(ISASuperIODevice),
+ .class_size = sizeof(ISASuperIOClass),
+ .class_init = vt82c686b_superio_class_init,
+};
+
static void vt82c686b_register_types(void)
{
type_register_static(&via_ac97_info);
type_register_static(&via_mc97_info);
type_register_static(&via_pm_info);
+ type_register_static(&via_superio_info);
type_register_static(&via_info);
}
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index f68c625666..02fb2fdcc4 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -22,17 +22,15 @@
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
-#include "hw/char/serial.h"
-#include "hw/block/fdc.h"
+#include "hw/dma/i8257.h"
+#include "hw/isa/superio.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
-#include "sysemu/block-backend.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/pci/pci.h"
-#include "sysemu/sysemu.h"
#include "audio/audio.h"
#include "qemu/log.h"
#include "hw/loader.h"
@@ -42,7 +40,6 @@
#include "hw/isa/vt82c686.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/timer/i8254.h"
-#include "sysemu/blockdev.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
#include "qemu/error-report.h"
@@ -75,8 +72,6 @@
#define FULONG2E_ATI_SLOT 6
#define FULONG2E_RTL8139_SLOT 7
-static ISADevice *pit;
-
static struct _loaderparams {
int ram_size;
const char *kernel_filename;
@@ -229,11 +224,40 @@ static const uint8_t eeprom_spd[0x80] = {
0x20,0x30,0x20
};
-/* Audio support */
-static void audio_init (PCIBus *pci_bus)
+static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc,
+ I2CBus **i2c_bus, ISABus **p_isa_bus)
{
- vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5));
- vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6));
+ qemu_irq *i8259;
+ ISABus *isa_bus;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+
+ isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0));
+ if (!isa_bus) {
+ fprintf(stderr, "vt82c686b_init error\n");
+ exit(1);
+ }
+ *p_isa_bus = isa_bus;
+ /* Interrupt controller */
+ /* The 8259 -> IP5 */
+ i8259 = i8259_init(isa_bus, intc);
+ isa_bus_irqs(isa_bus, i8259);
+ /* init other devices */
+ i8254_pit_init(isa_bus, 0x40, 0, NULL);
+ i8257_dma_init(isa_bus, 0);
+ /* Super I/O */
+ isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO);
+
+ ide_drive_get(hd, ARRAY_SIZE(hd));
+ vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1));
+
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci");
+ pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci");
+
+ *i2c_bus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(slot, 4), 0xeee1, NULL);
+
+ /* Audio support */
+ vt82c686b_ac97_init(pci_bus, PCI_DEVFN(slot, 5));
+ vt82c686b_mc97_init(pci_bus, PCI_DEVFN(slot, 6));
}
/* Network support */
@@ -266,11 +290,9 @@ static void mips_fulong2e_init(MachineState *machine)
MemoryRegion *bios = g_new(MemoryRegion, 1);
long bios_size;
int64_t kernel_entry;
- qemu_irq *i8259;
PCIBus *pci_bus;
ISABus *isa_bus;
I2CBus *smbus;
- DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
MIPSCPU *cpu;
CPUMIPSState *env;
@@ -332,46 +354,16 @@ static void mips_fulong2e_init(MachineState *machine)
/* North bridge, Bonito --> IP2 */
pci_bus = bonito_init((qemu_irq *)&(env->irq[2]));
- /* South bridge */
- ide_drive_get(hd, ARRAY_SIZE(hd));
+ /* South bridge -> IP5 */
+ vt82c686b_southbridge_init(pci_bus, FULONG2E_VIA_SLOT, env->irq[5],
+ &smbus, &isa_bus);
- isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0));
- if (!isa_bus) {
- error_report("vt82c686b_init error");
- exit(1);
- }
-
- /* Interrupt controller */
- /* The 8259 -> IP5 */
- i8259 = i8259_init(isa_bus, env->irq[5]);
- isa_bus_irqs(isa_bus, i8259);
-
- vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
- pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2),
- "vt82c686b-usb-uhci");
- pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3),
- "vt82c686b-usb-uhci");
-
- smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
- 0xeee1, NULL);
/* TODO: Populate SPD eeprom data. */
smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd));
- /* init other devices */
- pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
- DMA_init(isa_bus, 0);
-
- /* Super I/O */
- isa_create_simple(isa_bus, "i8042");
-
mc146818_rtc_init(isa_bus, 2000, NULL);
- serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
- parallel_hds_isa_init(isa_bus, 1);
-
- /* Sound card */
- audio_init(pci_bus);
- /* Network card */
+ /* Network card: RTL8139D */
network_init(pci_bus);
}
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index b09871a814..7223085547 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -27,7 +27,9 @@
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
#include "hw/i386/pc.h"
+#include "hw/dma/i8257.h"
#include "hw/char/serial.h"
+#include "hw/char/parallel.h"
#include "hw/isa/isa.h"
#include "hw/block/fdc.h"
#include "sysemu/sysemu.h"
@@ -41,7 +43,7 @@
#include "hw/timer/i8254.h"
#include "hw/display/vga.h"
#include "hw/audio/pcspk.h"
-#include "sysemu/block-backend.h"
+#include "hw/input/i8042.h"
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "sysemu/qtest.h"
@@ -147,6 +149,7 @@ static void mips_jazz_init(MachineState *machine,
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
+ ESPState *esp;
/* init CPUs */
cpu = MIPS_CPU(cpu_create(machine->cpu_type));
@@ -219,7 +222,7 @@ static void mips_jazz_init(MachineState *machine,
/* ISA devices */
i8259 = i8259_init(isa_bus, env->irq[4]);
isa_bus_irqs(isa_bus, i8259);
- DMA_init(isa_bus, 0);
+ i8257_dma_init(isa_bus, 0);
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
pcspk_init(isa_bus, pit);
@@ -278,9 +281,9 @@ static void mips_jazz_init(MachineState *machine,
}
/* SCSI adapter */
- esp_init(0x80002000, 0,
- rc4030_dma_read, rc4030_dma_write, dmas[0],
- qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
+ esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0],
+ qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
+ scsi_bus_legacy_handle_cmdline(&esp->bus);
/* Floppy */
for (n = 0; n < MAX_FD; n++) {
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 6f0deb99e7..f6513a4fd5 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -27,12 +27,12 @@
#include "cpu.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
+#include "hw/isa/superio.h"
+#include "hw/dma/i8257.h"
#include "hw/char/serial.h"
-#include "hw/block/fdc.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
-#include "sysemu/block-backend.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"
@@ -1002,10 +1002,8 @@ void mips_malta_init(MachineState *machine)
qemu_irq cbus_irq, i8259_irq;
int piix4_devfn;
I2CBus *smbus;
- int i;
DriveInfo *dinfo;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
- DriveInfo *fd[MAX_FD];
int fl_idx = 0;
int fl_sectors = bios_size >> 16;
int be;
@@ -1020,15 +1018,6 @@ void mips_malta_init(MachineState *machine)
qdev_init_nofail(dev);
- /* Make sure the first 3 serial ports are associated with a device. */
- for(i = 0; i < 3; i++) {
- if (!serial_hds[i]) {
- char label[32];
- snprintf(label, sizeof(label), "serial%d", i);
- serial_hds[i] = qemu_chr_new(label, "null");
- }
- }
-
/* create CPU */
mips_create_cpu(s, machine->cpu_type, &cbus_irq, &i8259_irq);
@@ -1059,16 +1048,19 @@ void mips_malta_init(MachineState *machine)
memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio);
}
- /* generate SPD EEPROM data */
- generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
- generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
-
#ifdef TARGET_WORDS_BIGENDIAN
be = 1;
#else
be = 0;
#endif
+
/* FPGA */
+
+ /* Make sure the second serial port is associated with a device. */
+ if (!serial_hds[2]) {
+ serial_hds[2] = qemu_chr_new("fpga-uart", "null");
+ }
+
/* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]);
@@ -1205,22 +1197,18 @@ void mips_malta_init(MachineState *machine)
pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci");
smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
isa_get_irq(NULL, 9), NULL, 0, NULL);
- smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
- g_free(smbus_eeprom_buf);
pit = i8254_pit_init(isa_bus, 0x40, 0, NULL);
- DMA_init(isa_bus, 0);
-
- /* Super I/O */
- isa_create_simple(isa_bus, "i8042");
-
+ i8257_dma_init(isa_bus, 0);
mc146818_rtc_init(isa_bus, 2000, NULL);
- serial_hds_isa_init(isa_bus, 0, 2);
- parallel_hds_isa_init(isa_bus, 1);
- for(i = 0; i < MAX_FD; i++) {
- fd[i] = drive_get(IF_FLOPPY, 0, i);
- }
- fdctrl_init_isa(isa_bus, fd);
+ /* generate SPD EEPROM data */
+ generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size);
+ generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]);
+ smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
+ g_free(smbus_eeprom_buf);
+
+ /* Super I/O: SMS FDC37M817 */
+ isa_create_simple(isa_bus, TYPE_FDC37M81X_SUPERIO);
/* Network card */
network_init(pci_bus);
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 830ee7732c..aeadc4a340 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -28,6 +28,7 @@
#include "hw/loader.h"
#include "elf.h"
#include "hw/timer/mc146818rtc.h"
+#include "hw/input/i8042.h"
#include "hw/timer/i8254.h"
#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
@@ -286,7 +287,7 @@ void mips_r4k_init(MachineState *machine)
hd[MAX_IDE_DEVS * i],
hd[MAX_IDE_DEVS * i + 1]);
- isa_create_simple(isa_bus, "i8042");
+ isa_create_simple(isa_bus, TYPE_I8042);
}
static void mips_machine_init(MachineClass *mc)
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index af1bd46b4b..454244f59e 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -406,6 +406,8 @@ static void macio_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_OTHERS << 8;
dc->props = macio_properties;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+ /* Reason: Uses serial_hds in macio_instance_init */
+ dc->user_creatable = false;
}
static const TypeInfo macio_oldworld_type_info = {
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index 9506f9b69f..6e297c5480 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -417,7 +417,33 @@ static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr)
static void imx_eth_update(IMXFECState *s)
{
- if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] & ENET_INT_TS_TIMER) {
+ /*
+ * Previous versions of qemu had the ENET_INT_MAC and ENET_INT_TS_TIMER
+ * interrupts swapped. This worked with older versions of Linux (4.14
+ * and older) since Linux associated both interrupt lines with Ethernet
+ * MAC interrupts. Specifically,
+ * - Linux 4.15 and later have separate interrupt handlers for the MAC and
+ * timer interrupts. Those versions of Linux fail with versions of QEMU
+ * with swapped interrupt assignments.
+ * - In linux 4.14, both interrupt lines were registered with the Ethernet
+ * MAC interrupt handler. As a result, all versions of qemu happen to
+ * work, though that is accidental.
+ * - In Linux 4.9 and older, the timer interrupt was registered directly
+ * with the Ethernet MAC interrupt handler. The MAC interrupt was
+ * redirected to a GPIO interrupt to work around erratum ERR006687.
+ * This was implemented using the SOC's IOMUX block. In qemu, this GPIO
+ * interrupt never fired since IOMUX is currently not supported in qemu.
+ * Linux instead received MAC interrupts on the timer interrupt.
+ * As a result, qemu versions with the swapped interrupt assignment work,
+ * albeit accidentally, but qemu versions with the correct interrupt
+ * assignment fail.
+ *
+ * To ensure that all versions of Linux work, generate ENET_INT_MAC
+ * interrrupts on both interrupt lines. This should be changed if and when
+ * qemu supports IOMUX.
+ */
+ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR] &
+ (ENET_INT_MAC | ENET_INT_TS_TIMER)) {
qemu_set_irq(s->irq[1], 1);
} else {
qemu_set_irq(s->irq[1], 0);
diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c
index e4007f6d7f..42053b2ca9 100644
--- a/hw/nios2/10m50_devboard.c
+++ b/hw/nios2/10m50_devboard.c
@@ -75,7 +75,7 @@ static void nios2_10m50_ghrd_init(MachineState *machine)
phys_ram_alias);
/* Create CPU -- FIXME */
- cpu = NIOS2_CPU(cpu_generic_init(TYPE_NIOS2_CPU, "nios2"));
+ cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU));
/* Register: CPU interrupt controller (PIC) */
cpu_irq = nios2_cpu_pic_init(cpu);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index f98efdc6e1..80bc45930d 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -1815,49 +1815,48 @@ PciInfoList *qmp_query_pci(Error **errp)
return head;
}
-static const char * const pci_nic_models[] = {
- "ne2k_pci",
- "i82551",
- "i82557b",
- "i82559er",
- "rtl8139",
- "e1000",
- "pcnet",
- "virtio",
- "sungem",
- NULL
-};
-
-static const char * const pci_nic_names[] = {
- "ne2k_pci",
- "i82551",
- "i82557b",
- "i82559er",
- "rtl8139",
- "e1000",
- "pcnet",
- "virtio-net-pci",
- "sungem",
- NULL
-};
-
/* Initialize a PCI NIC. */
PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
const char *default_model,
const char *default_devaddr)
{
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
+ GSList *list;
+ GPtrArray *pci_nic_models;
PCIBus *bus;
PCIDevice *pci_dev;
DeviceState *dev;
int devfn;
int i;
- if (qemu_show_nic_models(nd->model, pci_nic_models)) {
+ if (nd->model && !strcmp(nd->model, "virtio")) {
+ g_free(nd->model);
+ nd->model = g_strdup("virtio-net-pci");
+ }
+
+ list = object_class_get_list_sorted(TYPE_PCI_DEVICE, false);
+ pci_nic_models = g_ptr_array_new();
+ while (list) {
+ DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, list->data,
+ TYPE_DEVICE);
+ GSList *next;
+ if (test_bit(DEVICE_CATEGORY_NETWORK, dc->categories) &&
+ dc->user_creatable) {
+ const char *name = object_class_get_name(list->data);
+ g_ptr_array_add(pci_nic_models, (gpointer)name);
+ }
+ next = list->next;
+ g_slist_free_1(list);
+ list = next;
+ }
+ g_ptr_array_add(pci_nic_models, NULL);
+
+ if (qemu_show_nic_models(nd->model, (const char **)pci_nic_models->pdata)) {
exit(0);
}
- i = qemu_find_nic_model(nd, pci_nic_models, default_model);
+ i = qemu_find_nic_model(nd, (const char **)pci_nic_models->pdata,
+ default_model);
if (i < 0) {
exit(1);
}
@@ -1865,15 +1864,15 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
bus = pci_get_bus_devfn(&devfn, rootbus, devaddr);
if (!bus) {
error_report("Invalid PCI device address %s for device %s",
- devaddr, pci_nic_names[i]);
+ devaddr, nd->model);
exit(1);
}
- pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
+ pci_dev = pci_create(bus, devfn, nd->model);
dev = &pci_dev->qdev;
qdev_set_nic_properties(dev, nd);
qdev_init_nofail(dev);
-
+ g_ptr_array_free(pci_nic_models, true);
return pci_dev;
}
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 43c15d18c4..9a85a41362 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -916,7 +916,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
if (pci_bus) {
/* Register network interfaces. */
for (i = 0; i < nb_nics; i++) {
- pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio-net-pci", NULL);
}
}
@@ -1009,6 +1009,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
}
cur_base = loadaddr + payload_size;
+ if (cur_base < (32 * 1024 * 1024)) {
+ /* u-boot occupies memory up to 32MB, so load blobs above */
+ cur_base = (32 * 1024 * 1024);
+ }
/* Load bare kernel only if no bios/u-boot has been provided */
if (machine->kernel_filename && !kernel_as_payload) {
@@ -1025,11 +1029,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
cur_base += kernel_size;
}
- if (cur_base < (32 * 1024 * 1024)) {
- /* u-boot occupies memory up to 32MB, so load blobs above */
- cur_base = (32 * 1024 * 1024);
- }
-
/* Load initrd. */
if (machine->initrd_filename) {
initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK;
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index a749e2565d..2f5b6f651a 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -69,7 +69,6 @@
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
#include "hw/usb.h"
-#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "hw/sysbus.h"
#include "qemu/cutils.h"
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index 935493c966..10e291ca22 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -44,7 +44,6 @@
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
-#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
#include "qemu/cutils.h"
diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c
index ab2626a9de..b1307e6477 100644
--- a/hw/ppc/ppc440_pcix.c
+++ b/hw/ppc/ppc440_pcix.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qemu/log.h"
#include "hw/hw.h"
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
@@ -286,8 +287,9 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr,
break;
default:
- error_report("%s: unhandled PCI internal register 0x%lx", __func__,
- (unsigned long)addr);
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unhandled PCI internal register 0x%"HWADDR_PRIx"\n",
+ __func__, addr);
break;
}
}
@@ -377,8 +379,9 @@ static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr,
break;
default:
- error_report("%s: invalid PCI internal register 0x%lx", __func__,
- (unsigned long)addr);
+ qemu_log_mask(LOG_UNIMP,
+ "%s: invalid PCI internal register 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
val = 0;
}
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 096d4d4cfb..a1e7219db6 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -41,9 +41,9 @@
#include "hw/ide.h"
#include "hw/loader.h"
#include "hw/timer/mc146818rtc.h"
+#include "hw/input/i8042.h"
#include "hw/isa/pc87312.h"
#include "hw/net/ne2000-isa.h"
-#include "sysemu/block-backend.h"
#include "sysemu/arch_init.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
@@ -612,7 +612,7 @@ static void ppc_prep_init(MachineState *machine)
isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0"));
/* Super I/O (parallel + serial ports) */
- isa = isa_create(isa_bus, TYPE_PC87312);
+ isa = isa_create(isa_bus, TYPE_PC87312_SUPERIO);
dev = DEVICE(isa);
qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */
qdev_init_nofail(dev);
@@ -641,7 +641,6 @@ static void ppc_prep_init(MachineState *machine)
hd[2 * i],
hd[2 * i + 1]);
}
- isa_create_simple(isa_bus, "i8042");
cpu = POWERPC_CPU(first_cpu);
sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET];
@@ -771,7 +770,7 @@ static void ibm_40p_init(MachineState *machine)
/* add some more devices */
if (defaults_enabled()) {
- isa_create_simple(isa_bus, "i8042");
+ isa_create_simple(isa_bus, TYPE_I8042);
m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59"));
dev = DEVICE(isa_create(isa_bus, "cs4231a"));
@@ -788,7 +787,7 @@ static void ibm_40p_init(MachineState *machine)
qdev_prop_set_uint32(dev, "equipment", 0xc0);
qdev_init_nofail(dev);
- pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810");
+ lsi53c810_create(pci_bus, PCI_DEVFN(1, 0));
/* XXX: s3-trio at PCI_DEVFN(2, 0) */
pci_vga_init(pci_bus);
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 44a0670d11..2c0be8c898 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -2606,10 +2606,11 @@ static void spapr_machine_init(MachineState *machine)
NICInfo *nd = &nd_table[i];
if (!nd->model) {
- nd->model = g_strdup("ibmveth");
+ nd->model = g_strdup("spapr-vlan");
}
- if (strcmp(nd->model, "ibmveth") == 0) {
+ if (g_str_equal(nd->model, "spapr-vlan") ||
+ g_str_equal(nd->model, "ibmveth")) {
spapr_vlan_create(spapr->vio_bus, nd);
} else {
pci_nic_init_nofail(&nd_table[i], phb->bus, nd->model, NULL);
@@ -2854,6 +2855,11 @@ static void spapr_set_modern_hotplug_events(Object *obj, bool value,
spapr->use_hotplug_event_source = value;
}
+static bool spapr_get_msix_emulation(Object *obj, Error **errp)
+{
+ return true;
+}
+
static char *spapr_get_resize_hpt(Object *obj, Error **errp)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
@@ -2935,6 +2941,8 @@ static void spapr_instance_init(Object *obj)
object_property_set_description(obj, "vsmt",
"Virtual SMT: KVM behaves as if this were"
" the host's SMT mode", &error_abort);
+ object_property_add_bool(obj, "vfio-no-msix-emulation",
+ spapr_get_msix_emulation, NULL, NULL);
}
static void spapr_machine_finalizefn(Object *obj)
diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c
index adf07ef312..70b95550a8 100644
--- a/hw/s390x/s390-stattrib.c
+++ b/hw/s390x/s390-stattrib.c
@@ -183,15 +183,16 @@ static int cmma_save_setup(QEMUFile *f, void *opaque)
}
static void cmma_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
- uint64_t *non_postcopiable_pending,
- uint64_t *postcopiable_pending)
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
{
S390StAttribState *sas = S390_STATTRIB(opaque);
S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas);
long long res = sac->get_dirtycount(sas);
if (res >= 0) {
- *non_postcopiable_pending += res;
+ *res_precopy_only += res;
}
}
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 45975c21e8..64ec285826 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -618,11 +618,11 @@ static const MemoryRegionOps sysbus_esp_mem_ops = {
.valid.accepts = esp_mem_accepts,
};
-void esp_init(hwaddr espaddr, int it_shift,
- ESPDMAMemoryReadWriteFunc dma_memory_read,
- ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset,
- qemu_irq *dma_enable)
+ESPState *esp_init(hwaddr espaddr, int it_shift,
+ ESPDMAMemoryReadWriteFunc dma_memory_read,
+ ESPDMAMemoryReadWriteFunc dma_memory_write,
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable)
{
DeviceState *dev;
SysBusDevice *s;
@@ -644,6 +644,8 @@ void esp_init(hwaddr espaddr, int it_shift,
sysbus_mmio_map(s, 0, espaddr);
*reset = qdev_get_gpio_in(dev, 0);
*dma_enable = qdev_get_gpio_in(dev, 1);
+
+ return esp;
}
static const struct SCSIBusInfo esp_scsi_info = {
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index f3d4c4d230..160657f4b9 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -2279,3 +2279,10 @@ void lsi53c895a_create(PCIBus *bus)
scsi_bus_legacy_handle_cmdline(&s->bus);
}
+
+void lsi53c810_create(PCIBus *bus, int devfn)
+{
+ LSIState *s = LSI53C895A(pci_create_simple(bus, devfn, "lsi53c810"));
+
+ scsi_bus_legacy_handle_cmdline(&s->bus);
+}
diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c
index 3415229b5e..8ae39a76f4 100644
--- a/hw/scsi/mptendian.c
+++ b/hw/scsi/mptendian.c
@@ -24,7 +24,6 @@
#include "hw/hw.h"
#include "hw/pci/pci.h"
#include "sysemu/dma.h"
-#include "sysemu/block-backend.h"
#include "hw/pci/msi.h"
#include "qemu/iov.h"
#include "hw/scsi/scsi.h"
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 1eaeffc830..9646743a7d 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -944,7 +944,7 @@ static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
break;
case WRITE_SAME_10:
case WRITE_SAME_16:
- cmd->xfer = dev->blocksize;
+ cmd->xfer = buf[1] & 1 ? 0 : dev->blocksize;
break;
case READ_CAPACITY_10:
cmd->xfer = 8;
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 49d2559d93..5b7a48f5a5 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -704,6 +704,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
page_code);
return -1;
}
+ if (s->qdev.type == TYPE_DISK) {
+ int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
+ int max_io_sectors_blk =
+ max_transfer_blk / s->qdev.blocksize;
+
+ max_io_sectors =
+ MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
+
+ /* min_io_size and opt_io_size can't be greater than
+ * max_io_sectors */
+ min_io_size =
+ MIN_NON_ZERO(min_io_size, max_io_sectors);
+ opt_io_size =
+ MIN_NON_ZERO(opt_io_size, max_io_sectors);
+ }
/* required VPD size with unmap support */
buflen = 0x40;
memset(outbuf + 4, 0, buflen - 4);
@@ -1792,7 +1807,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
return;
}
- if (buffer_is_zero(inbuf, s->qdev.blocksize)) {
+ if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) {
int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
/* The request is used as the AIO opaque value, so add a ref. */
diff --git a/hw/sd/core.c b/hw/sd/core.c
index 3c6eae6c88..820345f704 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -21,7 +21,6 @@
#include "qemu/osdep.h"
#include "hw/qdev-core.h"
-#include "sysemu/block-backend.h"
#include "hw/sd/sd.h"
#include "trace.h"
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index 0f5804b3b4..6471aca25d 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -44,7 +44,6 @@
#include "hw/empty_slot.h"
#include "hw/loader.h"
#include "elf.h"
-#include "sysemu/block-backend.h"
#include "trace.h"
#include "qemu/cutils.h"
@@ -99,10 +98,6 @@ struct sun4m_hwdef {
uint8_t nvram_machine_id;
};
-void DMA_init(ISABus *bus, int high_page_enable)
-{
-}
-
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index da28ab9413..2044a52ded 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -34,7 +34,9 @@
#include "hw/pci-host/sabre.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
+#include "hw/char/parallel.h"
#include "hw/timer/m48t59.h"
+#include "hw/input/i8042.h"
#include "hw/block/fdc.h"
#include "net/net.h"
#include "qemu/timer.h"
@@ -89,10 +91,6 @@ typedef struct EbusState {
#define TYPE_EBUS "ebus"
#define EBUS(obj) OBJECT_CHECK(EbusState, (obj), TYPE_EBUS)
-void DMA_init(ISABus *bus, int high_page_enable)
-{
-}
-
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c
index ac75eb2128..8e61dfc3e6 100644
--- a/hw/tricore/tricore_testboard.c
+++ b/hw/tricore/tricore_testboard.c
@@ -28,9 +28,7 @@
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
-#include "sysemu/block-backend.h"
#include "exec/address-spaces.h"
-#include "hw/block/flash.h"
#include "elf.h"
#include "hw/tricore/tricore.h"
#include "qemu/error-report.h"
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index db26959a1d..830fe3face 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -20,6 +20,7 @@
#undef DEBUG_PUV3
#include "hw/unicore32/puv3.h"
+#include "hw/input/i8042.h"
#define KERNEL_LOAD_ADDR 0x03000000
#define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */
diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs
index c3ab9097f1..a2e7a0a7cf 100644
--- a/hw/vfio/Makefile.objs
+++ b/hw/vfio/Makefile.objs
@@ -1,6 +1,6 @@
ifeq ($(CONFIG_LINUX), y)
obj-$(CONFIG_SOFTMMU) += common.o
-obj-$(CONFIG_PCI) += pci.o pci-quirks.o
+obj-$(CONFIG_PCI) += pci.o pci-quirks.o display.o
obj-$(CONFIG_VFIO_CCW) += ccw.o
obj-$(CONFIG_SOFTMMU) += platform.o
obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index f895e3c335..5e84716218 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -544,18 +544,40 @@ static void vfio_listener_region_add(MemoryListener *listener,
llsize = int128_sub(llend, int128_make64(iova));
+ if (memory_region_is_ram_device(section->mr)) {
+ hwaddr pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1;
+
+ if ((iova & pgmask) || (int128_get64(llsize) & pgmask)) {
+ error_report("Region 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx
+ " is not aligned to 0x%"HWADDR_PRIx
+ " and cannot be mapped for DMA",
+ section->offset_within_region,
+ int128_getlo(section->size),
+ pgmask + 1);
+ return;
+ }
+ }
+
ret = vfio_dma_map(container, iova, int128_get64(llsize),
vaddr, section->readonly);
if (ret) {
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%m)",
container, iova, int128_get64(llsize), vaddr, ret);
+ if (memory_region_is_ram_device(section->mr)) {
+ /* Allow unexpected mappings not to be fatal for RAM devices */
+ return;
+ }
goto fail;
}
return;
fail:
+ if (memory_region_is_ram_device(section->mr)) {
+ error_report("failed to vfio_dma_map. pci p2p may not work");
+ return;
+ }
/*
* On the initfn path, store the first error in the container so we
* can gracefully fail. Runtime, there's not much we can do other
@@ -577,6 +599,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
hwaddr iova, end;
Int128 llend, llsize;
int ret;
+ bool try_unmap = true;
if (vfio_listener_skipped_section(section)) {
trace_vfio_listener_region_del_skip(
@@ -629,14 +652,34 @@ static void vfio_listener_region_del(MemoryListener *listener,
trace_vfio_listener_region_del(iova, end);
- ret = vfio_dma_unmap(container, iova, int128_get64(llsize));
- memory_region_unref(section->mr);
- if (ret) {
- error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
- "0x%"HWADDR_PRIx") = %d (%m)",
- container, iova, int128_get64(llsize), ret);
+ if (memory_region_is_ram_device(section->mr)) {
+ hwaddr pgmask;
+ VFIOHostDMAWindow *hostwin;
+ bool hostwin_found = false;
+
+ QLIST_FOREACH(hostwin, &container->hostwin_list, hostwin_next) {
+ if (hostwin->min_iova <= iova && end <= hostwin->max_iova) {
+ hostwin_found = true;
+ break;
+ }
+ }
+ assert(hostwin_found); /* or region_add() would have failed */
+
+ pgmask = (1ULL << ctz64(hostwin->iova_pgsizes)) - 1;
+ try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask));
}
+ if (try_unmap) {
+ ret = vfio_dma_unmap(container, iova, int128_get64(llsize));
+ if (ret) {
+ error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
+ "0x%"HWADDR_PRIx") = %d (%m)",
+ container, iova, int128_get64(llsize), ret);
+ }
+ }
+
+ memory_region_unref(section->mr);
+
if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
vfio_spapr_remove_window(container,
section->offset_within_address_space);
@@ -858,6 +901,13 @@ void vfio_region_finalize(VFIORegion *region)
g_free(region->mmaps);
trace_vfio_region_finalize(region->vbasedev->name, region->nr);
+
+ region->mem = NULL;
+ region->mmaps = NULL;
+ region->nr_mmaps = 0;
+ region->size = 0;
+ region->flags = 0;
+ region->nr = 0;
}
void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
@@ -1421,6 +1471,21 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type,
return -ENODEV;
}
+bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type)
+{
+ 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;
+}
+
/*
* Interfaces for IBM EEH (Enhanced Error Handling)
*/
diff --git a/hw/vfio/display.c b/hw/vfio/display.c
new file mode 100644
index 0000000000..7d727ce910
--- /dev/null
+++ b/hw/vfio/display.c
@@ -0,0 +1,347 @@
+/*
+ * display support for mdev based vgpu devices
+ *
+ * Copyright Red Hat, Inc. 2017
+ *
+ * Authors:
+ * Gerd Hoffmann
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <linux/vfio.h>
+#include <sys/ioctl.h>
+
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "qapi/error.h"
+#include "pci.h"
+
+#ifndef DRM_PLANE_TYPE_PRIMARY
+# define DRM_PLANE_TYPE_PRIMARY 1
+# define DRM_PLANE_TYPE_CURSOR 2
+#endif
+
+static void vfio_display_update_cursor(VFIODMABuf *dmabuf,
+ struct vfio_device_gfx_plane_info *plane)
+{
+ if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) {
+ dmabuf->pos_x = plane->x_pos;
+ dmabuf->pos_y = plane->y_pos;
+ dmabuf->pos_updates++;
+ }
+ if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) {
+ dmabuf->hot_x = plane->x_hot;
+ dmabuf->hot_y = plane->y_hot;
+ dmabuf->hot_updates++;
+ }
+}
+
+static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
+ uint32_t plane_type)
+{
+ VFIODisplay *dpy = vdev->dpy;
+ struct vfio_device_gfx_plane_info plane;
+ VFIODMABuf *dmabuf;
+ int fd, ret;
+
+ memset(&plane, 0, sizeof(plane));
+ plane.argsz = sizeof(plane);
+ plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF;
+ plane.drm_plane_type = plane_type;
+ ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
+ if (ret < 0) {
+ return NULL;
+ }
+ if (!plane.drm_format || !plane.size) {
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) {
+ if (dmabuf->dmabuf_id == plane.dmabuf_id) {
+ /* found in list, move to head, return it */
+ QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
+ QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
+ if (plane_type == DRM_PLANE_TYPE_CURSOR) {
+ vfio_display_update_cursor(dmabuf, &plane);
+ }
+ return dmabuf;
+ }
+ }
+
+ fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ dmabuf = g_new0(VFIODMABuf, 1);
+ dmabuf->dmabuf_id = plane.dmabuf_id;
+ dmabuf->buf.width = plane.width;
+ dmabuf->buf.height = plane.height;
+ dmabuf->buf.stride = plane.stride;
+ dmabuf->buf.fourcc = plane.drm_format;
+ dmabuf->buf.fd = fd;
+ if (plane_type == DRM_PLANE_TYPE_CURSOR) {
+ vfio_display_update_cursor(dmabuf, &plane);
+ }
+
+ QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
+ return dmabuf;
+}
+
+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);
+ g_free(dmabuf);
+}
+
+static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
+{
+ VFIODisplay *dpy = vdev->dpy;
+ VFIODMABuf *dmabuf, *tmp;
+ uint32_t keep = 5;
+
+ QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) {
+ if (keep > 0) {
+ keep--;
+ continue;
+ }
+ assert(dmabuf != dpy->dmabuf.primary);
+ vfio_display_free_one_dmabuf(dpy, dmabuf);
+ }
+}
+
+static void vfio_display_dmabuf_update(void *opaque)
+{
+ VFIOPCIDevice *vdev = opaque;
+ VFIODisplay *dpy = vdev->dpy;
+ VFIODMABuf *primary, *cursor;
+ bool free_bufs = false, new_cursor = false;;
+
+ primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
+ if (primary == NULL) {
+ return;
+ }
+
+ 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);
+ free_bufs = true;
+ }
+
+ cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR);
+ if (dpy->dmabuf.cursor != cursor) {
+ dpy->dmabuf.cursor = cursor;
+ new_cursor = true;
+ free_bufs = true;
+ }
+
+ 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,
+ cursor->hot_x, cursor->hot_y);
+ cursor->hot_updates = 0;
+ } else if (!cursor && new_cursor) {
+ dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
+ }
+
+ if (cursor && cursor->pos_updates) {
+ dpy_gl_cursor_position(dpy->con,
+ cursor->pos_x,
+ cursor->pos_y);
+ cursor->pos_updates = 0;
+ }
+
+ dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height);
+
+ if (free_bufs) {
+ vfio_display_free_dmabufs(vdev);
+ }
+}
+
+static const GraphicHwOps vfio_display_dmabuf_ops = {
+ .gfx_update = vfio_display_dmabuf_update,
+};
+
+static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
+{
+ if (!display_opengl) {
+ error_setg(errp, "vfio-display-dmabuf: opengl not available");
+ return -1;
+ }
+
+ vdev->dpy = g_new0(VFIODisplay, 1);
+ vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
+ &vfio_display_dmabuf_ops,
+ vdev);
+ return 0;
+}
+
+static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
+{
+ VFIODMABuf *dmabuf;
+
+ if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) {
+ return;
+ }
+
+ while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) {
+ vfio_display_free_one_dmabuf(dpy, dmabuf);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void vfio_display_region_update(void *opaque)
+{
+ VFIOPCIDevice *vdev = opaque;
+ VFIODisplay *dpy = vdev->dpy;
+ struct vfio_device_gfx_plane_info plane = {
+ .argsz = sizeof(plane),
+ .flags = VFIO_GFX_PLANE_TYPE_REGION
+ };
+ pixman_format_code_t format;
+ int ret;
+
+ ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
+ if (ret < 0) {
+ error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
+ strerror(errno));
+ return;
+ }
+ if (!plane.drm_format || !plane.size) {
+ return;
+ }
+ format = qemu_drm_format_to_pixman(plane.drm_format);
+ if (!format) {
+ return;
+ }
+
+ if (dpy->region.buffer.size &&
+ dpy->region.buffer.nr != plane.region_index) {
+ /* region changed */
+ vfio_region_exit(&dpy->region.buffer);
+ vfio_region_finalize(&dpy->region.buffer);
+ dpy->region.surface = NULL;
+ }
+
+ if (dpy->region.surface &&
+ (surface_width(dpy->region.surface) != plane.width ||
+ surface_height(dpy->region.surface) != plane.height ||
+ surface_format(dpy->region.surface) != format)) {
+ /* size changed */
+ dpy->region.surface = NULL;
+ }
+
+ if (!dpy->region.buffer.size) {
+ /* mmap region */
+ ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
+ &dpy->region.buffer,
+ plane.region_index,
+ "display");
+ if (ret != 0) {
+ error_report("%s: vfio_region_setup(%d): %s",
+ __func__, plane.region_index, strerror(-ret));
+ goto err;
+ }
+ ret = vfio_region_mmap(&dpy->region.buffer);
+ if (ret != 0) {
+ error_report("%s: vfio_region_mmap(%d): %s", __func__,
+ plane.region_index, strerror(-ret));
+ goto err;
+ }
+ assert(dpy->region.buffer.mmaps[0].mmap != NULL);
+ }
+
+ if (dpy->region.surface == NULL) {
+ /* create surface */
+ dpy->region.surface = qemu_create_displaysurface_from
+ (plane.width, plane.height, format,
+ plane.stride, dpy->region.buffer.mmaps[0].mmap);
+ dpy_gfx_replace_surface(dpy->con, dpy->region.surface);
+ }
+
+ /* full screen update */
+ dpy_gfx_update(dpy->con, 0, 0,
+ surface_width(dpy->region.surface),
+ surface_height(dpy->region.surface));
+ return;
+
+err:
+ vfio_region_exit(&dpy->region.buffer);
+ vfio_region_finalize(&dpy->region.buffer);
+}
+
+static const GraphicHwOps vfio_display_region_ops = {
+ .gfx_update = vfio_display_region_update,
+};
+
+static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
+{
+ vdev->dpy = g_new0(VFIODisplay, 1);
+ vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
+ &vfio_display_region_ops,
+ vdev);
+ return 0;
+}
+
+static void vfio_display_region_exit(VFIODisplay *dpy)
+{
+ if (!dpy->region.buffer.size) {
+ return;
+ }
+
+ vfio_region_exit(&dpy->region.buffer);
+ vfio_region_finalize(&dpy->region.buffer);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
+{
+ struct vfio_device_gfx_plane_info probe;
+ int ret;
+
+ memset(&probe, 0, sizeof(probe));
+ probe.argsz = sizeof(probe);
+ probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF;
+ ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
+ if (ret == 0) {
+ return vfio_display_dmabuf_init(vdev, errp);
+ }
+
+ memset(&probe, 0, sizeof(probe));
+ probe.argsz = sizeof(probe);
+ probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION;
+ ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
+ if (ret == 0) {
+ return vfio_display_region_init(vdev, errp);
+ }
+
+ if (vdev->display == ON_OFF_AUTO_AUTO) {
+ /* not an error in automatic mode */
+ return 0;
+ }
+
+ error_setg(errp, "vfio: device doesn't support any (known) display method");
+ return -1;
+}
+
+void vfio_display_finalize(VFIOPCIDevice *vdev)
+{
+ if (!vdev->dpy) {
+ return;
+ }
+
+ graphic_console_close(vdev->dpy->con);
+ vfio_display_dmabuf_exit(vdev->dpy);
+ vfio_display_region_exit(vdev->dpy);
+ g_free(vdev->dpy);
+}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 3ba3cbc146..b9bc6cd310 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1295,6 +1295,15 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region;
/*
+ * If the host driver allows mapping of a MSIX data, we are going to
+ * do map the entire BAR and emulate MSIX table on top of that.
+ */
+ if (vfio_has_region_cap(&vdev->vbasedev, region->nr,
+ VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) {
+ return;
+ }
+
+ /*
* We expect to find a single mmap covering the whole BAR, anything else
* means it's either unsupported or already setup.
*/
@@ -1572,6 +1581,19 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
*/
memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false);
+ /*
+ * The emulated machine may provide a paravirt interface for MSIX setup
+ * so it is not strictly necessary to emulate MSIX here. This becomes
+ * helpful when frequently accessed MMIO registers are located in
+ * subpages adjacent to the MSIX table but the MSIX data containing page
+ * cannot be mapped because of a host page size bigger than the MSIX table
+ * alignment.
+ */
+ if (object_property_get_bool(OBJECT(qdev_get_machine()),
+ "vfio-no-msix-emulation", NULL)) {
+ memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false);
+ }
+
return 0;
}
@@ -3015,6 +3037,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
}
}
+ if (vdev->display != ON_OFF_AUTO_OFF) {
+ ret = vfio_display_probe(vdev, errp);
+ if (ret) {
+ goto out_teardown;
+ }
+ }
+
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
vfio_setup_resetfn_quirk(vdev);
@@ -3035,6 +3064,7 @@ static void vfio_instance_finalize(Object *obj)
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev);
VFIOGroup *group = vdev->vbasedev.group;
+ vfio_display_finalize(vdev);
vfio_bars_finalize(vdev);
g_free(vdev->emulated_config_bits);
g_free(vdev->rom);
@@ -3123,6 +3153,8 @@ static void vfio_instance_init(Object *obj)
static Property vfio_pci_dev_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host),
DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev),
+ DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice,
+ display, ON_OFF_AUTO_AUTO),
DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice,
intx.mmap_timeout, 1100),
DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features,
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index f4aa13e021..629c875701 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -133,6 +133,7 @@ typedef struct VFIOPCIDevice {
#define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2
#define VFIO_FEATURE_ENABLE_IGD_OPREGION \
(1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT)
+ OnOffAuto display;
int32_t bootindex;
uint32_t igd_gms;
OffAutoPCIBAR msix_relo;
@@ -147,6 +148,7 @@ typedef struct VFIOPCIDevice {
bool no_kvm_msi;
bool no_kvm_msix;
bool no_geforce_quirks;
+ VFIODisplay *dpy;
} VFIOPCIDevice;
uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
@@ -174,4 +176,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
struct vfio_region_info *info,
Error **errp);
+int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
+void vfio_display_finalize(VFIOPCIDevice *vdev);
+
#endif /* HW_VFIO_VFIO_PCI_H */
diff --git a/include/block/accounting.h b/include/block/accounting.h
index b833d26d6c..d1f67b10dd 100644
--- a/include/block/accounting.h
+++ b/include/block/accounting.h
@@ -27,6 +27,7 @@
#include "qemu/timed-average.h"
#include "qemu/thread.h"
+#include "qapi/qapi-builtin-types.h"
typedef struct BlockAcctTimedStats BlockAcctTimedStats;
typedef struct BlockAcctStats BlockAcctStats;
@@ -45,6 +46,36 @@ struct BlockAcctTimedStats {
QSLIST_ENTRY(BlockAcctTimedStats) entries;
};
+typedef struct BlockLatencyHistogram {
+ /* The following histogram is represented like this:
+ *
+ * 5| *
+ * 4| *
+ * 3| * *
+ * 2| * * *
+ * 1| * * * *
+ * +------------------
+ * 10 50 100
+ *
+ * BlockLatencyHistogram histogram = {
+ * .nbins = 4,
+ * .boundaries = {10, 50, 100},
+ * .bins = {3, 1, 5, 2},
+ * };
+ *
+ * @boundaries array define histogram intervals as follows:
+ * [0, boundaries[0]), [boundaries[0], boundaries[1]), ...
+ * [boundaries[nbins-2], +inf)
+ *
+ * So, for example above, histogram intervals are:
+ * [0, 10), [10, 50), [50, 100), [100, +inf)
+ */
+ int nbins;
+ uint64_t *boundaries; /* @nbins-1 numbers here
+ (all boundaries, except 0 and +inf) */
+ uint64_t *bins;
+} BlockLatencyHistogram;
+
struct BlockAcctStats {
QemuMutex lock;
uint64_t nr_bytes[BLOCK_MAX_IOTYPE];
@@ -57,6 +88,7 @@ struct BlockAcctStats {
QSLIST_HEAD(, BlockAcctTimedStats) intervals;
bool account_invalid;
bool account_failed;
+ BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE];
};
typedef struct BlockAcctCookie {
@@ -82,5 +114,8 @@ void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type,
int64_t block_acct_idle_time_ns(BlockAcctStats *stats);
double block_acct_queue_depth(BlockAcctTimedStats *stats,
enum BlockAcctType type);
+int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
+ uint64List *boundaries);
+void block_latency_histograms_clear(BlockAcctStats *stats);
#endif
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 00403d9482..fc645dac68 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -63,6 +63,12 @@ typedef struct BlockJob {
bool cancelled;
/**
+ * Set to true if the job should abort immediately without waiting
+ * for data to be in sync.
+ */
+ bool force;
+
+ /**
* Counter for pause request. If non-zero, the block job is either paused,
* or if busy == true will pause itself as soon as possible.
*/
@@ -127,12 +133,10 @@ typedef struct BlockJob {
/** Reference count of the block job */
int refcnt;
- /* True if this job has reported completion by calling block_job_completed.
- */
+ /** True when job has reported completion by calling block_job_completed. */
bool completed;
- /* ret code passed to block_job_completed.
- */
+ /** ret code passed to block_job_completed. */
int ret;
/**
@@ -141,14 +145,28 @@ typedef struct BlockJob {
*/
QEMUTimer sleep_timer;
- /** Non-NULL if this job is part of a transaction */
+ /** Current state; See @BlockJobStatus for details. */
+ BlockJobStatus status;
+
+ /** True if this job should automatically finalize itself */
+ bool auto_finalize;
+
+ /** True if this job should automatically dismiss itself */
+ bool auto_dismiss;
+
BlockJobTxn *txn;
QLIST_ENTRY(BlockJob) txn_list;
} BlockJob;
typedef enum BlockJobCreateFlags {
+ /* Default behavior */
BLOCK_JOB_DEFAULT = 0x00,
+ /* BlockJob is not QMP-created and should not send QMP events */
BLOCK_JOB_INTERNAL = 0x01,
+ /* BlockJob requires manual finalize step */
+ BLOCK_JOB_MANUAL_FINALIZE = 0x02,
+ /* BlockJob requires manual dismiss step */
+ BLOCK_JOB_MANUAL_DISMISS = 0x04,
} BlockJobCreateFlags;
/**
@@ -218,10 +236,11 @@ void block_job_start(BlockJob *job);
/**
* block_job_cancel:
* @job: The job to be canceled.
+ * @force: Quit a job without waiting for data to be in sync.
*
* Asynchronously cancel the specified job.
*/
-void block_job_cancel(BlockJob *job);
+void block_job_cancel(BlockJob *job, bool force);
/**
* block_job_complete:
@@ -232,6 +251,32 @@ void block_job_cancel(BlockJob *job);
*/
void block_job_complete(BlockJob *job, Error **errp);
+
+/**
+ * block_job_finalize:
+ * @job: The job to fully commit and finish.
+ * @errp: Error object.
+ *
+ * For jobs that have finished their work and are pending
+ * awaiting explicit acknowledgement to commit their work,
+ * This will commit that work.
+ *
+ * FIXME: Make the below statement universally true:
+ * For jobs that support the manual workflow mode, all graph
+ * changes that occur as a result will occur after this command
+ * and before a successful reply.
+ */
+void block_job_finalize(BlockJob *job, Error **errp);
+
+/**
+ * block_job_dismiss:
+ * @job: The job to be dismissed.
+ * @errp: Error object.
+ *
+ * Remove a concluded job from the query list.
+ */
+void block_job_dismiss(BlockJob **job, Error **errp);
+
/**
* block_job_query:
* @job: The job to get information about.
@@ -247,7 +292,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
* Asynchronously pause the specified job.
* Do not allow a resume until a matching call to block_job_user_resume.
*/
-void block_job_user_pause(BlockJob *job);
+void block_job_user_pause(BlockJob *job, Error **errp);
/**
* block_job_paused:
@@ -264,7 +309,17 @@ bool block_job_user_paused(BlockJob *job);
* Resume the specified job.
* Must be paired with a preceding block_job_user_pause.
*/
-void block_job_user_resume(BlockJob *job);
+void block_job_user_resume(BlockJob *job, Error **errp);
+
+/**
+ * block_job_user_cancel:
+ * @job: The job to be cancelled.
+ * @force: Quit a job without waiting for data to be in sync.
+ *
+ * Cancels the specified job, but may refuse to do so if the
+ * operation isn't currently meaningful.
+ */
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp);
/**
* block_job_cancel_sync:
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
index c9b23b0cc9..642adce68b 100644
--- a/include/block/blockjob_int.h
+++ b/include/block/blockjob_int.h
@@ -54,6 +54,16 @@ struct BlockJobDriver {
void (*complete)(BlockJob *job, Error **errp);
/**
+ * If the callback is not NULL, prepare will be invoked when all the jobs
+ * belonging to the same transaction complete; or upon this job's completion
+ * if it is not in a transaction.
+ *
+ * This callback will not be invoked if the job has already failed.
+ * If it fails, abort and then clean will be called.
+ */
+ int (*prepare)(BlockJob *job);
+
+ /**
* If the callback is not NULL, it will be invoked when all the jobs
* belonging to the same transaction complete; or upon this job's
* completion if it is not in a transaction. Skipped if NULL.
@@ -114,10 +124,13 @@ struct BlockJobDriver {
* block_job_create:
* @job_id: The id of the newly-created job, or %NULL to have one
* generated automatically.
- * @job_type: The class object for the newly-created job.
+ * @driver: The class object for the newly-created job.
+ * @txn: The transaction this job belongs to, if any. %NULL otherwise.
* @bs: The block
* @perm, @shared_perm: Permissions to request for @bs
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
+ * @flags: Creation flags for the Block Job.
+ * See @BlockJobCreateFlags
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
* @errp: Error object.
@@ -132,7 +145,7 @@ struct BlockJobDriver {
* called from a wrapper that is specific to the job type.
*/
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
- BlockDriverState *bs, uint64_t perm,
+ BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
uint64_t shared_perm, int64_t speed, int flags,
BlockCompletionFunc *cb, void *opaque, Error **errp);
diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h
index 09efec609f..1ff8949b1b 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -21,6 +21,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap,
Error **errp);
+void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap);
BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs,
const char *name);
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap);
@@ -68,6 +69,8 @@ void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap);
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value);
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap,
bool persistent);
+void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked);
+
/* Functions that require manual locking. */
void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap);
@@ -87,10 +90,14 @@ bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap);
bool bdrv_has_readonly_bitmaps(BlockDriverState *bs);
bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap);
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap);
+bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap);
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs);
BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap);
char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp);
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start);
+BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
+ BdrvDirtyBitmap *bitmap,
+ Error **errp);
#endif
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 2285637e67..fcdcd54502 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -260,6 +260,7 @@ struct NBDExportInfo {
/* In-out fields, set by client before nbd_receive_negotiate() and
* updated by server results during nbd_receive_negotiate() */
bool structured_reply;
+ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */
/* Set by server results during nbd_receive_negotiate() */
uint64_t size;
@@ -267,6 +268,8 @@ struct NBDExportInfo {
uint32_t min_block;
uint32_t opt_block;
uint32_t max_block;
+
+ uint32_t meta_base_allocation_id;
};
typedef struct NBDExportInfo NBDExportInfo;
diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h
index 8928977897..1e13187767 100644
--- a/include/chardev/char-mux.h
+++ b/include/chardev/char-mux.h
@@ -27,8 +27,6 @@
#include "chardev/char.h"
#include "chardev/char-fe.h"
-extern bool muxes_realized;
-
#define MAX_MUX 4
#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
diff --git a/include/chardev/char.h b/include/chardev/char.h
index ebf1e0ba04..04de45795e 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -248,6 +248,8 @@ typedef struct ChardevClass {
void (*chr_set_echo)(Chardev *chr, bool echo);
void (*chr_set_fe_open)(Chardev *chr, int fe_open);
void (*chr_be_event)(Chardev *s, int event);
+ /* Return 0 if succeeded, 1 if failed */
+ int (*chr_machine_done)(Chardev *chr);
} ChardevClass;
Chardev *qemu_chardev_new(const char *id, const char *typename,
diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h
index 0b141683f0..f4fa94e966 100644
--- a/include/exec/cpu-all.h
+++ b/include/exec/cpu-all.h
@@ -159,8 +159,12 @@ extern unsigned long guest_base;
extern int have_guest_base;
extern unsigned long reserved_va;
-#define GUEST_ADDR_MAX (reserved_va ? reserved_va : \
+#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS
+#define GUEST_ADDR_MAX (~0ul)
+#else
+#define GUEST_ADDR_MAX (reserved_va ? reserved_va - 1 : \
(1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1)
+#endif
#else
#include "exec/hwaddr.h"
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index 191f2e962a..5de8c8a5af 100644
--- a/include/exec/cpu_ldst.h
+++ b/include/exec/cpu_ldst.h
@@ -51,15 +51,13 @@
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
#define g2h(x) ((void *)((unsigned long)(target_ulong)(x) + guest_base))
-#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS
-#define h2g_valid(x) 1
-#else
-#define h2g_valid(x) ({ \
- unsigned long __guest = (unsigned long)(x) - guest_base; \
- (__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) && \
- (!reserved_va || (__guest < reserved_va)); \
-})
-#endif
+#define guest_addr_valid(x) ((x) <= GUEST_ADDR_MAX)
+#define h2g_valid(x) guest_addr_valid((unsigned long)(x) - guest_base)
+
+static inline int guest_range_valid(unsigned long start, unsigned long len)
+{
+ return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1;
+}
#define h2g_nocheck(x) ({ \
unsigned long __ret = (unsigned long)(x) - guest_base; \
diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h
index 4758b4ae54..93248399ba 100644
--- a/include/hw/arm/bcm2836.h
+++ b/include/hw/arm/bcm2836.h
@@ -15,12 +15,19 @@
#include "hw/arm/bcm2835_peripherals.h"
#include "hw/intc/bcm2836_control.h"
-#define TYPE_BCM2836 "bcm2836"
-#define BCM2836(obj) OBJECT_CHECK(BCM2836State, (obj), TYPE_BCM2836)
+#define TYPE_BCM283X "bcm283x"
+#define BCM283X(obj) OBJECT_CHECK(BCM283XState, (obj), TYPE_BCM283X)
+
+#define BCM283X_NCPUS 4
-#define BCM2836_NCPUS 4
+/* These type names are for specific SoCs; other than instantiating
+ * them, code using these devices should always handle them via the
+ * BCM283x base class, so they have no BCM2836(obj) etc macros.
+ */
+#define TYPE_BCM2836 "bcm2836"
+#define TYPE_BCM2837 "bcm2837"
-typedef struct BCM2836State {
+typedef struct BCM283XState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
@@ -28,9 +35,21 @@ typedef struct BCM2836State {
char *cpu_type;
uint32_t enabled_cpus;
- ARMCPU cpus[BCM2836_NCPUS];
+ ARMCPU cpus[BCM283X_NCPUS];
BCM2836ControlState control;
BCM2835PeripheralState peripherals;
-} BCM2836State;
+} BCM283XState;
+
+typedef struct BCM283XInfo BCM283XInfo;
+
+typedef struct BCM283XClass {
+ DeviceClass parent_class;
+ const BCM283XInfo *info;
+} BCM283XClass;
+
+#define BCM283X_CLASS(klass) \
+ OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X)
+#define BCM283X_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X)
#endif /* BCM2836_H */
diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h
index ec6c509d74..06f8aaeda4 100644
--- a/include/hw/arm/fsl-imx6.h
+++ b/include/hw/arm/fsl-imx6.h
@@ -438,8 +438,8 @@ typedef struct FslIMX6State {
#define FSL_IMX6_HDMI_MASTER_IRQ 115
#define FSL_IMX6_HDMI_CEC_IRQ 116
#define FSL_IMX6_MLB150_LOW_IRQ 117
-#define FSL_IMX6_ENET_MAC_1588_IRQ 118
-#define FSL_IMX6_ENET_MAC_IRQ 119
+#define FSL_IMX6_ENET_MAC_IRQ 118
+#define FSL_IMX6_ENET_MAC_1588_IRQ 119
#define FSL_IMX6_PCIE1_IRQ 120
#define FSL_IMX6_PCIE2_IRQ 121
#define FSL_IMX6_PCIE3_IRQ 122
diff --git a/include/hw/boards.h b/include/hw/boards.h
index efb0a9edfd..a609239112 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -243,6 +243,7 @@ struct MachineState {
bool suppress_vmdesc;
bool enforce_config_section;
bool enable_graphics;
+ char *memory_encryption;
ram_addr_t ram_size;
ram_addr_t maxram_size;
@@ -251,7 +252,6 @@ struct MachineState {
char *kernel_filename;
char *kernel_cmdline;
char *initrd_filename;
- const char *cpu_model;
const char *cpu_type;
AccelState *accelerator;
CPUArchIdList *possible_cpus;
diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h
index baeec3183f..5b99cee7cf 100644
--- a/include/hw/char/imx_serial.h
+++ b/include/hw/char/imx_serial.h
@@ -67,6 +67,8 @@
#define UCR2_RXEN (1<<1) /* Receiver enable */
#define UCR2_SRST (1<<0) /* Reset complete */
+#define UCR4_TCEN BIT(3) /* TX complete interrupt enable */
+
#define UTS1_TXEMPTY (1<<6)
#define UTS1_RXEMPTY (1<<5)
#define UTS1_TXFULL (1<<4)
@@ -95,6 +97,7 @@ typedef struct IMXSerialState {
uint32_t ubmr;
uint32_t ubrc;
uint32_t ucr3;
+ uint32_t ucr4;
qemu_irq irq;
CharBackend chr;
diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h
new file mode 100644
index 0000000000..d6dd62fb9f
--- /dev/null
+++ b/include/hw/char/parallel.h
@@ -0,0 +1,14 @@
+#ifndef HW_PARALLEL_H
+#define HW_PARALLEL_H
+
+#include "exec/memory.h"
+#include "hw/isa/isa.h"
+#include "chardev/char.h"
+
+void parallel_hds_isa_init(ISABus *bus, int n);
+
+bool parallel_mm_init(MemoryRegion *address_space,
+ hwaddr base, int it_shift, qemu_irq irq,
+ Chardev *chr);
+
+#endif
diff --git a/include/hw/isa/i8257.h b/include/hw/dma/i8257.h
index 88a2766a3f..2cab50bb6c 100644
--- a/include/hw/isa/i8257.h
+++ b/include/hw/dma/i8257.h
@@ -1,6 +1,10 @@
#ifndef HW_I8257_H
#define HW_I8257_H
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "exec/ioport.h"
+
#define TYPE_I8257 "i8257"
typedef struct I8257Regs {
@@ -40,4 +44,6 @@ typedef struct I8257State {
PortioList portio_pageh;
} I8257State;
+void i8257_dma_init(ISABus *bus, bool high_page_enable);
+
#endif
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index bb49165fe0..ffee8413f0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -114,6 +114,7 @@ struct PCMachineClass {
/* Device configuration: */
bool pci_enabled;
bool kvmclock_enabled;
+ const char *default_nic_model;
/* Compat options: */
@@ -151,14 +152,6 @@ struct PCMachineClass {
#define PC_MACHINE_CLASS(klass) \
OBJECT_CLASS_CHECK(PCMachineClass, (klass), TYPE_PC_MACHINE)
-/* parallel.c */
-
-void parallel_hds_isa_init(ISABus *bus, int n);
-
-bool parallel_mm_init(MemoryRegion *address_space,
- hwaddr base, int it_shift, qemu_irq irq,
- Chardev *chr);
-
/* i8259.c */
extern DeviceState *isa_pic;
@@ -196,15 +189,6 @@ void vmport_register(unsigned char command, VMPortReadFunc *func, void *opaque);
void vmmouse_get_data(uint32_t *data);
void vmmouse_set_data(const uint32_t *data);
-/* pckbd.c */
-#define I8042_A20_LINE "a20"
-
-void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
- MemoryRegion *region, ram_addr_t size,
- hwaddr mask);
-void i8042_isa_mouse_fake_event(void *opaque);
-void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out);
-
/* pc.c */
extern int fd_bootchk;
@@ -248,7 +232,7 @@ void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd);
void pc_cmos_init(PCMachineState *pcms,
BusState *ide0, BusState *ide1,
ISADevice *s);
-void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus);
+void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus);
void pc_pci_device_init(PCIBus *pci_bus);
typedef void (*cpu_set_smm_t)(int smm, void *arg);
@@ -617,7 +601,7 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
#define PC_COMPAT_2_2 \
HW_COMPAT_2_2 \
- PC_CPU_MODEL_IDS("2.3.0") \
+ PC_CPU_MODEL_IDS("2.2.0") \
{\
.driver = "kvm64" "-" TYPE_X86_CPU,\
.property = "vme",\
diff --git a/include/hw/input/i8042.h b/include/hw/input/i8042.h
new file mode 100644
index 0000000000..f6ff146364
--- /dev/null
+++ b/include/hw/input/i8042.h
@@ -0,0 +1,24 @@
+/*
+ * QEMU PS/2 Controller
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef HW_INPUT_I8042_H
+#define HW_INPUT_I8042_H
+
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+
+#define TYPE_I8042 "i8042"
+
+#define I8042_A20_LINE "a20"
+
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+ MemoryRegion *region, ram_addr_t size,
+ hwaddr mask);
+void i8042_isa_mouse_fake_event(void *opaque);
+void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out);
+
+#endif /* HW_INPUT_I8042_H */
diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h
index 95593408ef..b9dbab24b4 100644
--- a/include/hw/isa/isa.h
+++ b/include/hw/isa/isa.h
@@ -151,6 +151,4 @@ static inline ISABus *isa_bus_from_device(ISADevice *d)
return ISA_BUS(qdev_get_parent_bus(DEVICE(d)));
}
-/* i8257.c */
-void DMA_init(ISABus *bus, int high_page_enable);
#endif
diff --git a/include/hw/isa/pc87312.h b/include/hw/isa/pc87312.h
index bf74470d40..e16263d4b1 100644
--- a/include/hw/isa/pc87312.h
+++ b/include/hw/isa/pc87312.h
@@ -25,32 +25,22 @@
#ifndef QEMU_PC87312_H
#define QEMU_PC87312_H
-#include "hw/isa/isa.h"
+#include "hw/isa/superio.h"
-#define TYPE_PC87312 "pc87312"
-#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312)
+#define TYPE_PC87312_SUPERIO "pc87312"
+#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312_SUPERIO)
typedef struct PC87312State {
- ISADevice dev;
+ /*< private >*/
+ ISASuperIODevice parent_dev;
+ /*< public >*/
- uint32_t iobase;
+ uint16_t iobase;
uint8_t config; /* initial configuration */
struct {
ISADevice *dev;
- } parallel;
-
- struct {
- ISADevice *dev;
- } uart[2];
-
- struct {
- ISADevice *dev;
- } fdc;
-
- struct {
- ISADevice *dev;
} ide;
MemoryRegion io;
diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h
new file mode 100644
index 0000000000..f9ba29aa30
--- /dev/null
+++ b/include/hw/isa/superio.h
@@ -0,0 +1,60 @@
+/*
+ * Generic ISA Super I/O
+ *
+ * Copyright (c) 2018 Philippe Mathieu-Daudé
+ *
+ * This code is licensed under the GNU GPLv2 and later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_ISA_SUPERIO_H
+#define HW_ISA_SUPERIO_H
+
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "hw/isa/isa.h"
+
+#define TYPE_ISA_SUPERIO "isa-superio"
+#define ISA_SUPERIO(obj) \
+ OBJECT_CHECK(ISASuperIODevice, (obj), TYPE_ISA_SUPERIO)
+#define ISA_SUPERIO_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ISASuperIOClass, (obj), TYPE_ISA_SUPERIO)
+#define ISA_SUPERIO_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ISASuperIOClass, (klass), TYPE_ISA_SUPERIO)
+
+typedef struct ISASuperIODevice {
+ /*< private >*/
+ ISADevice parent_obj;
+ /*< public >*/
+
+ ISADevice *parallel[MAX_PARALLEL_PORTS];
+ ISADevice *serial[MAX_SERIAL_PORTS];
+ ISADevice *floppy;
+ ISADevice *kbc;
+ ISADevice *ide;
+} ISASuperIODevice;
+
+typedef struct ISASuperIOFuncs {
+ size_t count;
+ bool (*is_enabled)(ISASuperIODevice *sio, uint8_t index);
+ uint16_t (*get_iobase)(ISASuperIODevice *sio, uint8_t index);
+ unsigned int (*get_irq)(ISASuperIODevice *sio, uint8_t index);
+ unsigned int (*get_dma)(ISASuperIODevice *sio, uint8_t index);
+} ISASuperIOFuncs;
+
+typedef struct ISASuperIOClass {
+ /*< private >*/
+ ISADeviceClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+
+ ISASuperIOFuncs parallel;
+ ISASuperIOFuncs serial;
+ ISASuperIOFuncs floppy;
+ ISASuperIOFuncs ide;
+} ISASuperIOClass;
+
+#define TYPE_FDC37M81X_SUPERIO "fdc37m81x-superio"
+#define TYPE_SMC37C669_SUPERIO "smc37c669-superio"
+
+#endif /* HW_ISA_SUPERIO_H */
diff --git a/include/hw/isa/vt82c686.h b/include/hw/isa/vt82c686.h
index 471b5e9e53..c3c2b6e786 100644
--- a/include/hw/isa/vt82c686.h
+++ b/include/hw/isa/vt82c686.h
@@ -1,8 +1,10 @@
#ifndef HW_VT82C686_H
#define HW_VT82C686_H
+#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
+
/* vt82c686.c */
-ISABus *vt82c686b_init(PCIBus * bus, int devfn);
+ISABus *vt82c686b_isa_init(PCIBus * bus, int devfn);
void vt82c686b_ac97_init(PCIBus *bus, int devfn);
void vt82c686b_mc97_init(PCIBus *bus, int devfn);
I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index e28f3fa33e..a9c3ee5aa2 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -707,6 +707,7 @@ PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name);
PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name);
void lsi53c895a_create(PCIBus *bus);
+void lsi53c810_create(PCIBus *bus, int devfn);
qemu_irq pci_allocate_irq(PCIDevice *pci_dev);
void pci_set_irq(PCIDevice *pci_dev, int level);
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index 3b160f858c..93fdaced67 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -7,11 +7,6 @@
/* esp.c */
#define ESP_MAX_DEVS 7
typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
-void esp_init(hwaddr espaddr, int it_shift,
- ESPDMAMemoryReadWriteFunc dma_memory_read,
- ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset,
- qemu_irq *dma_enable);
#define ESP_REGS 16
#define TI_BUFSZ 16
@@ -136,6 +131,11 @@ typedef struct {
#define TCHI_FAS100A 0x4
#define TCHI_AM53C974 0x12
+ESPState *esp_init(hwaddr espaddr, int it_shift,
+ ESPDMAMemoryReadWriteFunc dma_memory_read,
+ ESPDMAMemoryReadWriteFunc dma_memory_write,
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable);
void esp_dma_enable(ESPState *s, int irq, int level);
void esp_request_cancelled(SCSIRequest *req);
void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid);
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index f3a2ac9fee..d9360148e6 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -26,6 +26,7 @@
#include "exec/memory.h"
#include "qemu/queue.h"
#include "qemu/notify.h"
+#include "ui/console.h"
#ifdef CONFIG_LINUX
#include <linux/vfio.h>
#endif
@@ -142,6 +143,27 @@ typedef struct VFIOGroup {
QLIST_ENTRY(VFIOGroup) container_next;
} VFIOGroup;
+typedef struct VFIODMABuf {
+ QemuDmaBuf buf;
+ uint32_t pos_x, pos_y, pos_updates;
+ uint32_t hot_x, hot_y, hot_updates;
+ int dmabuf_id;
+ QTAILQ_ENTRY(VFIODMABuf) next;
+} VFIODMABuf;
+
+typedef struct VFIODisplay {
+ QemuConsole *con;
+ struct {
+ VFIORegion buffer;
+ DisplaySurface *surface;
+ } region;
+ struct {
+ QTAILQ_HEAD(, VFIODMABuf) bufs;
+ VFIODMABuf *primary;
+ VFIODMABuf *cursor;
+ } dmabuf;
+} VFIODisplay;
+
void vfio_put_base_device(VFIODevice *vbasedev);
void vfio_disable_irqindex(VFIODevice *vbasedev, int index);
void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
@@ -171,6 +193,7 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index,
struct vfio_region_info **info);
int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type,
uint32_t subtype, struct vfio_region_info **info);
+bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type);
#endif
extern const MemoryListener vfio_prereg_listener;
diff --git a/include/migration/misc.h b/include/migration/misc.h
index 77fd4f587c..4ebf24c6c2 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -56,4 +56,7 @@ bool migration_has_failed(MigrationState *);
bool migration_in_postcopy_after_devices(MigrationState *);
void migration_global_dump(Monitor *mon);
+/* migration/block-dirty-bitmap.c */
+void dirty_bitmap_mig_init(void);
+
#endif
diff --git a/include/migration/register.h b/include/migration/register.h
index f4f7bdc177..f6f12f9b1a 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -26,6 +26,15 @@ typedef struct SaveVMHandlers {
bool (*is_active)(void *opaque);
bool (*has_postcopy)(void *opaque);
+ /* is_active_iterate
+ * If it is not NULL then qemu_savevm_state_iterate will skip iteration if
+ * it returns false. For example, it is needed for only-postcopy-states,
+ * which needs to be handled by qemu_savevm_state_setup and
+ * qemu_savevm_state_pending, but do not need iterations until not in
+ * postcopy stage.
+ */
+ bool (*is_active_iterate)(void *opaque);
+
/* This runs outside the iothread lock in the migration case, and
* within the lock in the savevm case. The callback had better only
* use data that is local to the migration thread or protected
@@ -37,8 +46,21 @@ typedef struct SaveVMHandlers {
int (*save_setup)(QEMUFile *f, void *opaque);
void (*save_live_pending)(QEMUFile *f, void *opaque,
uint64_t threshold_size,
- uint64_t *non_postcopiable_pending,
- uint64_t *postcopiable_pending);
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only);
+ /* Note for save_live_pending:
+ * - res_precopy_only is for data which must be migrated in precopy phase
+ * or in stopped state, in other words - before target vm start
+ * - res_compatible is for data which may be migrated in any phase
+ * - res_postcopy_only is for data which must be migrated in postcopy phase
+ * or in stopped state, in other words - after source vm stop
+ *
+ * Sum of res_postcopy_only, res_compatible and res_postcopy_only is the
+ * whole amount of pending data.
+ */
+
+
LoadStateHandler *load_state;
int (*load_setup)(QEMUFile *f, void *opaque);
int (*load_cleanup)(void *opaque);
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index d1024d4bdc..0cb0538a31 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -16,7 +16,7 @@ extern Monitor *cur_mon;
bool monitor_cur_is_qmp(void);
-void monitor_init_qmp_commands(void);
+void monitor_init_globals(void);
void monitor_init(Chardev *chr, int flags);
void monitor_cleanup(void);
diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h
index 1e694b5ecf..ffb4652f71 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -20,8 +20,9 @@ typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
typedef enum QmpCommandOptions
{
- QCO_NO_OPTIONS = 0x0,
- QCO_NO_SUCCESS_RESP = 0x1,
+ QCO_NO_OPTIONS = 0x0,
+ QCO_NO_SUCCESS_RESP = (1U << 0),
+ QCO_ALLOW_OOB = (1U << 1),
} QmpCommandOptions;
typedef struct QmpCommand
@@ -47,6 +48,8 @@ bool qmp_command_is_enabled(const QmpCommand *cmd);
const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *err);
+QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp);
+bool qmp_is_oob(QDict *dict);
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h
index 629c508d34..b9a44a1bfe 100644
--- a/include/qapi/qmp/qbool.h
+++ b/include/qapi/qmp/qbool.h
@@ -23,7 +23,6 @@ struct QBool {
QBool *qbool_from_bool(bool value);
bool qbool_get_bool(const QBool *qb);
-QBool *qobject_to_qbool(const QObject *obj);
bool qbool_is_equal(const QObject *x, const QObject *y);
void qbool_destroy_obj(QObject *obj);
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 7c6d844549..2cc3e906f7 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -39,7 +39,6 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value);
void qdict_del(QDict *qdict, const char *key);
int qdict_haskey(const QDict *qdict, const char *key);
QObject *qdict_get(const QDict *qdict, const char *key);
-QDict *qobject_to_qdict(const QObject *obj);
bool qdict_is_equal(const QObject *x, const QObject *y);
void qdict_iter(const QDict *qdict,
void (*iter)(const char *key, QObject *obj, void *opaque),
diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h
index 5fd976a398..5c673acb06 100644
--- a/include/qapi/qmp/qlist.h
+++ b/include/qapi/qmp/qlist.h
@@ -53,7 +53,6 @@ QObject *qlist_pop(QList *qlist);
QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist);
size_t qlist_size(const QList *qlist);
-QList *qobject_to_qlist(const QObject *obj);
bool qlist_is_equal(const QObject *x, const QObject *y);
void qlist_destroy_obj(QObject *obj);
diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
index 56f9d97bd9..c0676d5daf 100644
--- a/include/qapi/qmp/qlit.h
+++ b/include/qapi/qmp/qlit.h
@@ -20,7 +20,7 @@ typedef struct QLitDictEntry QLitDictEntry;
typedef struct QLitObject QLitObject;
struct QLitObject {
- int type;
+ QType type;
union {
bool qbool;
int64_t qnum;
@@ -50,4 +50,6 @@ struct QLitDictEntry {
bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs);
+QObject *qobject_from_qlit(const QLitObject *qlit);
+
#endif /* QLIT_H */
diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h
index 15e3971c7f..3e47475b2c 100644
--- a/include/qapi/qmp/qnum.h
+++ b/include/qapi/qmp/qnum.h
@@ -68,7 +68,6 @@ double qnum_get_double(QNum *qn);
char *qnum_to_string(QNum *qn);
-QNum *qobject_to_qnum(const QObject *obj);
bool qnum_is_equal(const QObject *x, const QObject *y);
void qnum_destroy_obj(QObject *obj);
diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h
index 012439a2e3..e022707578 100644
--- a/include/qapi/qmp/qobject.h
+++ b/include/qapi/qmp/qobject.h
@@ -50,6 +50,21 @@ struct QObject {
#define QDECREF(obj) \
qobject_decref(obj ? QOBJECT(obj) : NULL)
+/* Required for qobject_to() */
+#define QTYPE_CAST_TO_QNull QTYPE_QNULL
+#define QTYPE_CAST_TO_QNum QTYPE_QNUM
+#define QTYPE_CAST_TO_QString QTYPE_QSTRING
+#define QTYPE_CAST_TO_QDict QTYPE_QDICT
+#define QTYPE_CAST_TO_QList QTYPE_QLIST
+#define QTYPE_CAST_TO_QBool QTYPE_QBOOL
+
+QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7,
+ "The QTYPE_CAST_TO_* list needs to be extended");
+
+#define qobject_to(type, obj) ({ \
+ QObject *_tmp = qobject_check_type(obj, glue(QTYPE_CAST_TO_, type)); \
+ _tmp ? container_of(_tmp, type, base) : (type *)NULL; })
+
/* Initialize an object to default values */
static inline void qobject_init(QObject *obj, QType type)
{
@@ -102,4 +117,18 @@ static inline QType qobject_type(const QObject *obj)
return obj->type;
}
+/**
+ * qobject_check_type(): Helper function for the qobject_to() macro.
+ * Return @obj, but only if @obj is not NULL and @type is equal to
+ * @obj's type. Return NULL otherwise.
+ */
+static inline QObject *qobject_check_type(const QObject *obj, QType type)
+{
+ if (obj && qobject_type(obj) == type) {
+ return (QObject *)obj;
+ } else {
+ return NULL;
+ }
+}
+
#endif /* QOBJECT_H */
diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h
index 98070ef3d6..30ae260a7f 100644
--- a/include/qapi/qmp/qstring.h
+++ b/include/qapi/qmp/qstring.h
@@ -27,10 +27,11 @@ QString *qstring_from_str(const char *str);
QString *qstring_from_substr(const char *str, int start, int end);
size_t qstring_get_length(const QString *qstring);
const char *qstring_get_str(const QString *qstring);
+const char *qstring_get_try_str(const QString *qstring);
+const char *qobject_get_try_str(const QObject *qstring);
void qstring_append_int(QString *qstring, int64_t value);
void qstring_append(QString *qstring, const char *str);
void qstring_append_chr(QString *qstring, int c);
-QString *qobject_to_qstring(const QObject *obj);
bool qstring_is_equal(const QObject *x, const QObject *y);
void qstring_destroy_obj(QObject *obj);
diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h
index 2cbe6a4f16..9f762695d1 100644
--- a/include/qemu/compiler.h
+++ b/include/qemu/compiler.h
@@ -82,15 +82,21 @@
int:(x) ? -1 : 1; \
}
+/* QEMU_BUILD_BUG_MSG() emits the message given if _Static_assert is
+ * supported; otherwise, it will be omitted from the compiler error
+ * message (but as it remains present in the source code, it can still
+ * be useful when debugging). */
#if defined(CONFIG_STATIC_ASSERT)
-#define QEMU_BUILD_BUG_ON(x) _Static_assert(!(x), "not expecting: " #x)
+#define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg)
#elif defined(__COUNTER__)
-#define QEMU_BUILD_BUG_ON(x) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \
+#define QEMU_BUILD_BUG_MSG(x, msg) typedef QEMU_BUILD_BUG_ON_STRUCT(x) \
glue(qemu_build_bug_on__, __COUNTER__) __attribute__((unused))
#else
-#define QEMU_BUILD_BUG_ON(x)
+#define QEMU_BUILD_BUG_MSG(x, msg)
#endif
+#define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x)
+
#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \
sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)))
diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index f0878eaafa..a663340b23 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -126,6 +126,10 @@ time_t mktimegm(struct tm *tm);
int qemu_fdatasync(int fd);
int fcntl_setfl(int fd, int flag);
int qemu_parse_fd(const char *param);
+int qemu_strtoi(const char *nptr, const char **endptr, int base,
+ int *result);
+int qemu_strtoui(const char *nptr, const char **endptr, int base,
+ unsigned int *result);
int qemu_strtol(const char *nptr, const char **endptr, int base,
long *result);
int qemu_strtoul(const char *nptr, const char **endptr, int base,
diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h
index f19413d649..22876d1428 100644
--- a/include/qemu/rcu.h
+++ b/include/qemu/rcu.h
@@ -27,6 +27,7 @@
#include "qemu/thread.h"
#include "qemu/queue.h"
#include "qemu/atomic.h"
+#include "qemu/sys_membarrier.h"
#ifdef __cplusplus
extern "C" {
@@ -79,7 +80,10 @@ static inline void rcu_read_lock(void)
}
ctr = atomic_read(&rcu_gp_ctr);
- atomic_xchg(&p_rcu_reader->ctr, ctr);
+ atomic_set(&p_rcu_reader->ctr, ctr);
+
+ /* Write p_rcu_reader->ctr before reading RCU-protected pointers. */
+ smp_mb_placeholder();
}
static inline void rcu_read_unlock(void)
@@ -91,7 +95,15 @@ static inline void rcu_read_unlock(void)
return;
}
- atomic_xchg(&p_rcu_reader->ctr, 0);
+ /* Ensure that the critical section is seen to precede the
+ * store to p_rcu_reader->ctr. Together with the following
+ * smp_mb_placeholder(), this ensures writes to p_rcu_reader->ctr
+ * are sequentially consistent.
+ */
+ atomic_store_release(&p_rcu_reader->ctr, 0);
+
+ /* Write p_rcu_reader->ctr before reading p_rcu_reader->waiting. */
+ smp_mb_placeholder();
if (unlikely(atomic_read(&p_rcu_reader->waiting))) {
atomic_set(&p_rcu_reader->waiting, false);
qemu_event_set(&rcu_gp_event);
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index e88d4c37ab..8140fea685 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -12,6 +12,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
#include "qapi/qapi-types-sockets.h"
/* misc helpers */
+bool fd_is_socket(int fd);
int qemu_socket(int domain, int type, int protocol);
int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int socket_set_cork(int fd, int v);
diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h
new file mode 100644
index 0000000000..316e3dc4a2
--- /dev/null
+++ b/include/qemu/sys_membarrier.h
@@ -0,0 +1,27 @@
+/*
+ * Process-global memory barriers
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ */
+
+#ifndef QEMU_SYS_MEMBARRIER_H
+#define QEMU_SYS_MEMBARRIER_H 1
+
+#ifdef CONFIG_MEMBARRIER
+/* Only block reordering at the compiler level in the performance-critical
+ * side. The slow side forces processor-level ordering on all other cores
+ * through a system call.
+ */
+extern void smp_mb_global_init(void);
+extern void smp_mb_global(void);
+#define smp_mb_placeholder() barrier()
+#else
+/* Keep it simple, execute a real memory barrier on both sides. */
+static inline void smp_mb_global_init(void) {}
+#define smp_mb_global() smp_mb()
+#define smp_mb_placeholder() smp_mb()
+#endif
+
+#endif
diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 3b5a54b014..39ea907e65 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -251,6 +251,20 @@ bool qemu_clock_run_timers(QEMUClockType type);
*/
bool qemu_clock_run_all_timers(void);
+/**
+ * qemu_clock_get_last:
+ *
+ * Returns last clock query time.
+ */
+uint64_t qemu_clock_get_last(QEMUClockType type);
+/**
+ * qemu_clock_set_last:
+ *
+ * Sets last clock query time.
+ */
+void qemu_clock_set_last(QEMUClockType type, uint64_t last);
+
+
/*
* QEMUTimerList
*/
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index dc6d4956a8..14e45c4282 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -662,8 +662,7 @@ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model);
CPUState *cpu_create(const char *typename);
/**
- * cpu_parse_cpu_model:
- * @typename: The CPU base type or CPU type.
+ * parse_cpu_model:
* @cpu_model: The model string including optional parameters.
*
* processes optional parameters and registers them as global properties
@@ -671,18 +670,7 @@ CPUState *cpu_create(const char *typename);
* Returns: type of CPU to create or prints error and terminates process
* if an error occurred.
*/
-const char *cpu_parse_cpu_model(const char *typename, const char *cpu_model);
-
-/**
- * cpu_generic_init:
- * @typename: The CPU base type.
- * @cpu_model: The model string including optional parameters.
- *
- * Instantiates a CPU, processes optional parameters and realizes the CPU.
- *
- * Returns: A #CPUState or %NULL if an error occurred.
- */
-CPUState *cpu_generic_init(const char *typename, const char *cpu_model);
+const char *parse_cpu_model(const char *cpu_model);
/**
* cpu_has_work:
diff --git a/include/qom/object.h b/include/qom/object.h
index 4f07090db0..96ce81bc5e 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -914,6 +914,17 @@ GSList *object_class_get_list(const char *implements_type,
bool include_abstract);
/**
+ * object_class_get_list_sorted:
+ * @implements_type: The type to filter for, including its derivatives.
+ * @include_abstract: Whether to include abstract classes.
+ *
+ * Returns: A singly-linked list of the classes in alphabetical
+ * case-insensitive order.
+ */
+GSList *object_class_get_list_sorted(const char *implements_type,
+ bool include_abstract);
+
+/**
* object_ref:
* @obj: the object
*
diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h
new file mode 100644
index 0000000000..11912fde24
--- /dev/null
+++ b/include/standard-headers/drm/drm_fourcc.h
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2011 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 DRM_FOURCC_H
+#define DRM_FOURCC_H
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
+ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */
+
+/* color index */
+#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */
+
+/* 8 bpp Red */
+#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
+
+/* 16 bpp Red */
+#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
+
+/* 16 bpp RG */
+#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
+#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
+
+/* 32 bpp RG */
+#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
+#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
+
+/* 8 bpp RGB */
+#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */
+#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */
+
+/* 16 bpp RGB */
+#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */
+#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */
+#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */
+#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */
+
+#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */
+#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */
+#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */
+#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */
+
+#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */
+#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */
+#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */
+#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */
+
+#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */
+#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */
+#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */
+#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */
+
+#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
+#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */
+
+/* 24 bpp RGB */
+#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
+#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */
+
+/* 32 bpp RGB */
+#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
+#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
+#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */
+
+#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */
+#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */
+#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
+
+#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */
+#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */
+#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */
+#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */
+
+#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */
+#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */
+#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */
+#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */
+
+/* packed YCbCr */
+#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */
+#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */
+#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */
+#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */
+
+#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
+
+/*
+ * 2 plane RGB + A
+ * index 0 = RGB plane, same format as the corresponding non _A8 format has
+ * index 1 = A plane, [7:0] A
+ */
+#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8')
+#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8')
+#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8')
+#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8')
+#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8')
+#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8')
+#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8')
+#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8')
+
+/*
+ * 2 plane YCbCr
+ * index 0 = Y plane, [7:0] Y
+ * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian
+ * or
+ * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian
+ */
+#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */
+#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
+#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
+
+/*
+ * 3 plane YCbCr
+ * index 0: Y plane, [7:0] Y
+ * index 1: Cb plane, [7:0] Cb
+ * index 2: Cr plane, [7:0] Cr
+ * or
+ * index 1: Cr plane, [7:0] Cr
+ * index 2: Cb plane, [7:0] Cb
+ */
+#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */
+#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
+#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
+
+
+/*
+ * Format Modifiers:
+ *
+ * Format modifiers describe, typically, a re-ordering or modification
+ * of the data in a plane of an FB. This can be used to express tiled/
+ * swizzled formats, or compression, or a combination of the two.
+ *
+ * The upper 8 bits of the format modifier are a vendor-id as assigned
+ * below. The lower 56 bits are assigned as vendor sees fit.
+ */
+
+/* Vendor Ids: */
+#define DRM_FORMAT_MOD_NONE 0
+#define DRM_FORMAT_MOD_VENDOR_NONE 0
+#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01
+#define DRM_FORMAT_MOD_VENDOR_AMD 0x02
+#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03
+#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04
+#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05
+#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06
+#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07
+/* add more to the end as needed */
+
+#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1)
+
+#define fourcc_mod_code(vendor, val) \
+ ((((uint64_t)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL))
+
+/*
+ * Format Modifier tokens:
+ *
+ * When adding a new token please document the layout with a code comment,
+ * similar to the fourcc codes above. drm_fourcc.h is considered the
+ * authoritative source for all of these.
+ */
+
+/*
+ * Invalid Modifier
+ *
+ * This modifier can be used as a sentinel to terminate the format modifiers
+ * list, or to initialize a variable with an invalid modifier. It might also be
+ * used to report an error back to userspace for certain APIs.
+ */
+#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED)
+
+/*
+ * Linear Layout
+ *
+ * Just plain linear layout. Note that this is different from no specifying any
+ * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl),
+ * which tells the driver to also take driver-internal information into account
+ * and so might actually result in a tiled framebuffer.
+ */
+#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
+
+/* Intel framebuffer modifiers */
+
+/*
+ * Intel X-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb)
+ * in row-major layout. Within the tile bytes are laid out row-major, with
+ * a platform-dependent stride. On top of that the memory can apply
+ * platform-depending swizzling of some higher address bits into bit6.
+ *
+ * This format is highly platforms specific and not useful for cross-driver
+ * sharing. It exists since on a given platform it does uniquely identify the
+ * layout in a simple way for i915-specific userspace.
+ */
+#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1)
+
+/*
+ * Intel Y-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb)
+ * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes)
+ * chunks column-major, with a platform-dependent height. On top of that the
+ * memory can apply platform-depending swizzling of some higher address bits
+ * into bit6.
+ *
+ * This format is highly platforms specific and not useful for cross-driver
+ * sharing. It exists since on a given platform it does uniquely identify the
+ * layout in a simple way for i915-specific userspace.
+ */
+#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2)
+
+/*
+ * Intel Yf-tiling layout
+ *
+ * This is a tiled layout using 4Kb tiles in row-major layout.
+ * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which
+ * are arranged in four groups (two wide, two high) with column-major layout.
+ * Each group therefore consits out of four 256 byte units, which are also laid
+ * out as 2x2 column-major.
+ * 256 byte units are made out of four 64 byte blocks of pixels, producing
+ * either a square block or a 2:1 unit.
+ * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width
+ * in pixel depends on the pixel depth.
+ */
+#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3)
+
+/*
+ * Intel color control surface (CCS) for render compression
+ *
+ * The framebuffer format must be one of the 8:8:8:8 RGB formats.
+ * The main surface will be plane index 0 and must be Y/Yf-tiled,
+ * the CCS will be plane index 1.
+ *
+ * Each CCS tile matches a 1024x512 pixel area of the main surface.
+ * To match certain aspects of the 3D hardware the CCS is
+ * considered to be made up of normal 128Bx32 Y tiles, Thus
+ * the CCS pitch must be specified in multiples of 128 bytes.
+ *
+ * In reality the CCS tile appears to be a 64Bx64 Y tile, composed
+ * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks.
+ * But that fact is not relevant unless the memory is accessed
+ * directly.
+ */
+#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4)
+#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5)
+
+/*
+ * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks
+ *
+ * Macroblocks are laid in a Z-shape, and each pixel data is following the
+ * standard NV12 style.
+ * As for NV12, an image is the result of two frame buffers: one for Y,
+ * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer).
+ * Alignment requirements are (for each buffer):
+ * - multiple of 128 pixels for the width
+ * - multiple of 32 pixels for the height
+ *
+ * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html
+ */
+#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1)
+
+/* Vivante framebuffer modifiers */
+
+/*
+ * Vivante 4x4 tiling layout
+ *
+ * This is a simple tiled layout using tiles of 4x4 pixels in a row-major
+ * layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1)
+
+/*
+ * Vivante 64x64 super-tiling layout
+ *
+ * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile
+ * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row-
+ * major layout.
+ *
+ * For more information: see
+ * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2)
+
+/*
+ * Vivante 4x4 tiling layout for dual-pipe
+ *
+ * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a
+ * different base address. Offsets from the base addresses are therefore halved
+ * compared to the non-split tiled layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3)
+
+/*
+ * Vivante 64x64 super-tiling layout for dual-pipe
+ *
+ * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile
+ * starts at a different base address. Offsets from the base addresses are
+ * therefore halved compared to the non-split super-tiled layout.
+ */
+#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4)
+
+/* NVIDIA frame buffer modifiers */
+
+/*
+ * Tegra Tiled Layout, used by Tegra 2, 3 and 4.
+ *
+ * Pixels are arranged in simple tiles of 16 x 16 bytes.
+ */
+#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1)
+
+/*
+ * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later
+ *
+ * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked
+ * vertically by a power of 2 (1 to 32 GOBs) to form a block.
+ *
+ * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape.
+ *
+ * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically.
+ * Valid values are:
+ *
+ * 0 == ONE_GOB
+ * 1 == TWO_GOBS
+ * 2 == FOUR_GOBS
+ * 3 == EIGHT_GOBS
+ * 4 == SIXTEEN_GOBS
+ * 5 == THIRTYTWO_GOBS
+ *
+ * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format
+ * in full detail.
+ */
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \
+ fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf))
+
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \
+ fourcc_mod_code(NVIDIA, 0x10)
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \
+ fourcc_mod_code(NVIDIA, 0x11)
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \
+ fourcc_mod_code(NVIDIA, 0x12)
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \
+ fourcc_mod_code(NVIDIA, 0x13)
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \
+ fourcc_mod_code(NVIDIA, 0x14)
+#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \
+ fourcc_mod_code(NVIDIA, 0x15)
+
+/*
+ * Broadcom VC4 "T" format
+ *
+ * This is the primary layout that the V3D GPU can texture from (it
+ * can't do linear). The T format has:
+ *
+ * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4
+ * pixels at 32 bit depth.
+ *
+ * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually
+ * 16x16 pixels).
+ *
+ * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On
+ * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows
+ * they're (TR, BR, BL, TL), where bottom left is start of memory.
+ *
+ * - an image made of 4k tiles in rows either left-to-right (even rows of 4k
+ * tiles) or right-to-left (odd rows of 4k tiles).
+ */
+#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* DRM_FOURCC_H */
diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h
index 79841b543f..9e6a8ba4ce 100644
--- a/include/standard-headers/linux/input-event-codes.h
+++ b/include/standard-headers/linux/input-event-codes.h
@@ -594,6 +594,7 @@
#define BTN_DPAD_RIGHT 0x223
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
+#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h
index bc3e6d3d5b..939b62775c 100644
--- a/include/standard-headers/linux/input.h
+++ b/include/standard-headers/linux/input.h
@@ -18,10 +18,21 @@
/*
* The event structure itself
+ * Note that __USE_TIME_BITS64 is defined by libc based on
+ * application's request to use 64 bit time_t.
*/
struct input_event {
+#if (HOST_LONG_BITS != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL)
struct timeval time;
+#define input_event_sec time.tv_sec
+#define input_event_usec time.tv_usec
+#else
+ __kernel_ulong_t __sec;
+ __kernel_ulong_t __usec;
+#define input_event_sec __sec
+#define input_event_usec __usec
+#endif
uint16_t type;
uint16_t code;
int32_t value;
diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h
index 70c2b2ade0..0c79eac5e9 100644
--- a/include/standard-headers/linux/pci_regs.h
+++ b/include/standard-headers/linux/pci_regs.h
@@ -622,15 +622,19 @@
* safely.
*/
#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
+#define PCI_EXP_DEVCAP2_COMP_TMOUT_DIS 0x00000010 /* Completion Timeout Disable supported */
#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */
#define PCI_EXP_DEVCAP2_ATOMIC_ROUTE 0x00000040 /* Atomic Op routing */
-#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* Atomic 64-bit compare */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP32 0x00000080 /* 32b AtomicOp completion */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */
#define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */
#define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */
#define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */
#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */
#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */
+#define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */
#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */
#define PCI_EXP_DEVCTL2_ATOMIC_REQ 0x0040 /* Set Atomic requests */
#define PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK 0x0080 /* Block atomic egress */
@@ -966,26 +970,28 @@
/* Downstream Port Containment */
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
-#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */
-#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
-#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
-#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
-#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0xF00 /* RP PIO log size */
+#define PCI_EXP_DPC_IRQ 0x001F /* Interrupt Message Number */
+#define PCI_EXP_DPC_CAP_RP_EXT 0x0020 /* Root Port Extensions */
+#define PCI_EXP_DPC_CAP_POISONED_TLP 0x0040 /* Poisoned TLP Egress Blocking Supported */
+#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x0080 /* Software Triggering Supported */
+#define PCI_EXP_DPC_RP_PIO_LOG_SIZE 0x0F00 /* RP PIO Log Size */
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
#define PCI_EXP_DPC_CTL 6 /* DPC control */
-#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */
-#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */
+#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */
+#define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */
#define PCI_EXP_DPC_STATUS 8 /* DPC Status */
-#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */
-#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */
-#define PCI_EXP_DPC_RP_BUSY 0x10 /* Root Port Busy */
+#define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */
+#define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */
+#define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */
+#define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */
+#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */
#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
#define PCI_EXP_DPC_RP_PIO_STATUS 0x0C /* RP PIO Status */
-#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO MASK */
+#define PCI_EXP_DPC_RP_PIO_MASK 0x10 /* RP PIO Mask */
#define PCI_EXP_DPC_RP_PIO_SEVERITY 0x14 /* RP PIO Severity */
#define PCI_EXP_DPC_RP_PIO_SYSERROR 0x18 /* RP PIO SysError */
#define PCI_EXP_DPC_RP_PIO_EXCEPTION 0x1C /* RP PIO Exception */
diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h
index f1dc05df25..d26e72bc6b 100644
--- a/include/standard-headers/linux/virtio_ring.h
+++ b/include/standard-headers/linux/virtio_ring.h
@@ -78,7 +78,7 @@ struct vring_avail {
__virtio16 ring[];
};
-/* u32 is used here for ids for padding reasons. */
+/* uint32_t is used here for ids for padding reasons. */
struct vring_used_elem {
/* Index of start of used descriptor chain. */
__virtio32 id;
diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h
index 0d0f7a8aca..07a820d337 100644
--- a/include/standard-headers/rdma/vmw_pvrdma-abi.h
+++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
/*
* Copyright (c) 2012-2016 VMware, Inc. All rights reserved.
*
@@ -51,12 +52,14 @@
#define PVRDMA_UVERBS_ABI_VERSION 3 /* ABI Version. */
#define PVRDMA_UAR_HANDLE_MASK 0x00FFFFFF /* Bottom 24 bits. */
#define PVRDMA_UAR_QP_OFFSET 0 /* QP doorbell. */
-#define PVRDMA_UAR_QP_SEND BIT(30) /* Send bit. */
-#define PVRDMA_UAR_QP_RECV BIT(31) /* Recv bit. */
+#define PVRDMA_UAR_QP_SEND (1 << 30) /* Send bit. */
+#define PVRDMA_UAR_QP_RECV (1 << 31) /* Recv bit. */
#define PVRDMA_UAR_CQ_OFFSET 4 /* CQ doorbell. */
-#define PVRDMA_UAR_CQ_ARM_SOL BIT(29) /* Arm solicited bit. */
-#define PVRDMA_UAR_CQ_ARM BIT(30) /* Arm bit. */
-#define PVRDMA_UAR_CQ_POLL BIT(31) /* Poll bit. */
+#define PVRDMA_UAR_CQ_ARM_SOL (1 << 29) /* Arm solicited bit. */
+#define PVRDMA_UAR_CQ_ARM (1 << 30) /* Arm bit. */
+#define PVRDMA_UAR_CQ_POLL (1 << 31) /* Poll bit. */
+#define PVRDMA_UAR_SRQ_OFFSET 8 /* SRQ doorbell. */
+#define PVRDMA_UAR_SRQ_RECV (1 << 30) /* Recv bit. */
enum pvrdma_wr_opcode {
PVRDMA_WR_RDMA_WRITE,
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index 85002ac49a..23669c4d5a 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -231,6 +231,23 @@ int kvm_destroy_vcpu(CPUState *cpu);
*/
bool kvm_arm_supports_user_irq(void);
+/**
+ * kvm_memcrypt_enabled - return boolean indicating whether memory encryption
+ * is enabled
+ * Returns: 1 memory encryption is enabled
+ * 0 memory encryption is disabled
+ */
+bool kvm_memcrypt_enabled(void);
+
+/**
+ * kvm_memcrypt_encrypt_data: encrypt the memory range
+ *
+ * Return: 1 failed to encrypt the range
+ * 0 succesfully encrypted memory region
+ */
+int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len);
+
+
#ifdef NEED_CPU_H
#include "cpu.h"
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index c0204e641c..3ced6bc231 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -48,6 +48,19 @@ extern ReplayMode replay_mode;
/* Name of the initial VM snapshot */
extern char *replay_snapshot;
+/* Replay locking
+ *
+ * The locks are needed to protect the shared structures and log file
+ * when doing record/replay. They also are the main sync-point between
+ * the main-loop thread and the vCPU thread. This was a role
+ * previously filled by the BQL which has been busy trying to reduce
+ * its impact across the code. This ensures blocks of events stay
+ * sequential and reproducible.
+ */
+
+void replay_mutex_lock(void);
+void replay_mutex_unlock(void);
+
/* Replay process control functions */
/*! Enables recording or saving event log with specified parameters */
@@ -166,5 +179,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size);
/*! Called at the start of execution.
Loads or saves initial vmstate depending on execution mode. */
void replay_vmstate_init(void);
+/*! Called to ensure that replay state is consistent and VM snapshot
+ can be created */
+bool replay_can_snapshot(void);
#endif
diff --git a/include/sysemu/sev.h b/include/sysemu/sev.h
new file mode 100644
index 0000000000..98c1ec8d38
--- /dev/null
+++ b/include/sysemu/sev.h
@@ -0,0 +1,21 @@
+/*
+ * QEMU Secure Encrypted Virutualization (SEV) support
+ *
+ * Copyright: Advanced Micro Devices, 2016-2018
+ *
+ * Authors:
+ * Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_SEV_H
+#define QEMU_SEV_H
+
+#include "sysemu/kvm.h"
+
+void *sev_guest_init(const char *id);
+int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len);
+#endif
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 356bfdc1c1..2b42151c63 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -88,6 +88,8 @@ void qemu_system_guest_panicked(GuestPanicInformation *info);
void qemu_add_exit_notifier(Notifier *notify);
void qemu_remove_exit_notifier(Notifier *notify);
+extern bool machine_init_done;
+
void qemu_add_machine_init_done_notifier(Notifier *notify);
void qemu_remove_machine_init_done_notifier(Notifier *notify);
diff --git a/include/ui/console.h b/include/ui/console.h
index 5fca9afcbc..6d2c052068 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -386,6 +386,7 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
void graphic_console_set_hwops(QemuConsole *con,
const GraphicHwOps *hw_ops,
void *opaque);
+void graphic_console_close(QemuConsole *con);
void graphic_hw_update(QemuConsole *con);
void graphic_hw_invalidate(QemuConsole *con);
@@ -398,6 +399,7 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index);
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
uint32_t head, Error **errp);
+QemuConsole *qemu_console_lookup_unused(void);
bool qemu_console_is_visible(QemuConsole *con);
bool qemu_console_is_graphic(QemuConsole *con);
bool qemu_console_is_fixedsize(QemuConsole *con);
diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 4a67e01232..b7c82d17fc 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -33,6 +33,8 @@
# define PIXMAN_BE_r8g8b8a8 PIXMAN_r8g8b8a8
# define PIXMAN_BE_x8b8g8r8 PIXMAN_x8b8g8r8
# define PIXMAN_BE_a8b8g8r8 PIXMAN_a8b8g8r8
+# define PIXMAN_LE_r8g8b8 PIXMAN_b8g8r8
+# define PIXMAN_LE_a8r8g8b8 PIXMAN_b8g8r8a8
# define PIXMAN_LE_x8r8g8b8 PIXMAN_b8g8r8x8
#else
# define PIXMAN_BE_r8g8b8 PIXMAN_b8g8r8
@@ -44,6 +46,8 @@
# define PIXMAN_BE_r8g8b8a8 PIXMAN_a8b8g8r8
# define PIXMAN_BE_x8b8g8r8 PIXMAN_r8g8b8x8
# define PIXMAN_BE_a8b8g8r8 PIXMAN_r8g8b8a8
+# define PIXMAN_LE_r8g8b8 PIXMAN_r8g8b8
+# define PIXMAN_LE_a8r8g8b8 PIXMAN_a8r8g8b8
# define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8
#endif
@@ -51,6 +55,7 @@
PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format);
pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian);
+pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format);
int qemu_pixman_get_type(int rshift, int gshift, int bshift);
pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf);
bool qemu_pixman_check_format(DisplayChangeListener *dcl,
diff --git a/io/channel-util.c b/io/channel-util.c
index 0fb4bd0837..423d79845a 100644
--- a/io/channel-util.c
+++ b/io/channel-util.c
@@ -24,19 +24,6 @@
#include "io/channel-socket.h"
-static bool fd_is_socket(int fd)
-{
- int optval;
- socklen_t optlen;
- optlen = sizeof(optval);
- return qemu_getsockopt(fd,
- SOL_SOCKET,
- SO_TYPE,
- (char *)&optval,
- &optlen) == 0;
-}
-
-
QIOChannel *qio_channel_new_fd(int fd,
Error **errp)
{
diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h
index 637b7263cb..833ed9a16a 100644
--- a/linux-headers/asm-powerpc/kvm.h
+++ b/linux-headers/asm-powerpc/kvm.h
@@ -632,6 +632,8 @@ struct kvm_ppc_cpu_char {
#define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc)
#define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd)
+#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe)
+
/* Transactional Memory checkpointed state:
* This is all GPRs, all VSX regs and a subset of SPRs
*/
diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h
index 36abf58582..0c08edcfcd 100644
--- a/linux-headers/asm-powerpc/unistd.h
+++ b/linux-headers/asm-powerpc/unistd.h
@@ -395,5 +395,8 @@
#define __NR_pwritev2 381
#define __NR_kexec_file_load 382
#define __NR_statx 383
+#define __NR_pkey_alloc 384
+#define __NR_pkey_free 385
+#define __NR_pkey_mprotect 386
#endif /* _ASM_POWERPC_UNISTD_H_ */
diff --git a/linux-headers/asm-s390/unistd.h b/linux-headers/asm-s390/unistd.h
index 99223b874a..27b8b211c8 100644
--- a/linux-headers/asm-s390/unistd.h
+++ b/linux-headers/asm-s390/unistd.h
@@ -8,405 +8,10 @@
#ifndef _ASM_S390_UNISTD_H_
#define _ASM_S390_UNISTD_H_
-/*
- * This file contains the system call numbers.
- */
-
-#define __NR_exit 1
-#define __NR_fork 2
-#define __NR_read 3
-#define __NR_write 4
-#define __NR_open 5
-#define __NR_close 6
-#define __NR_restart_syscall 7
-#define __NR_creat 8
-#define __NR_link 9
-#define __NR_unlink 10
-#define __NR_execve 11
-#define __NR_chdir 12
-#define __NR_mknod 14
-#define __NR_chmod 15
-#define __NR_lseek 19
-#define __NR_getpid 20
-#define __NR_mount 21
-#define __NR_umount 22
-#define __NR_ptrace 26
-#define __NR_alarm 27
-#define __NR_pause 29
-#define __NR_utime 30
-#define __NR_access 33
-#define __NR_nice 34
-#define __NR_sync 36
-#define __NR_kill 37
-#define __NR_rename 38
-#define __NR_mkdir 39
-#define __NR_rmdir 40
-#define __NR_dup 41
-#define __NR_pipe 42
-#define __NR_times 43
-#define __NR_brk 45
-#define __NR_signal 48
-#define __NR_acct 51
-#define __NR_umount2 52
-#define __NR_ioctl 54
-#define __NR_fcntl 55
-#define __NR_setpgid 57
-#define __NR_umask 60
-#define __NR_chroot 61
-#define __NR_ustat 62
-#define __NR_dup2 63
-#define __NR_getppid 64
-#define __NR_getpgrp 65
-#define __NR_setsid 66
-#define __NR_sigaction 67
-#define __NR_sigsuspend 72
-#define __NR_sigpending 73
-#define __NR_sethostname 74
-#define __NR_setrlimit 75
-#define __NR_getrusage 77
-#define __NR_gettimeofday 78
-#define __NR_settimeofday 79
-#define __NR_symlink 83
-#define __NR_readlink 85
-#define __NR_uselib 86
-#define __NR_swapon 87
-#define __NR_reboot 88
-#define __NR_readdir 89
-#define __NR_mmap 90
-#define __NR_munmap 91
-#define __NR_truncate 92
-#define __NR_ftruncate 93
-#define __NR_fchmod 94
-#define __NR_getpriority 96
-#define __NR_setpriority 97
-#define __NR_statfs 99
-#define __NR_fstatfs 100
-#define __NR_socketcall 102
-#define __NR_syslog 103
-#define __NR_setitimer 104
-#define __NR_getitimer 105
-#define __NR_stat 106
-#define __NR_lstat 107
-#define __NR_fstat 108
-#define __NR_lookup_dcookie 110
-#define __NR_vhangup 111
-#define __NR_idle 112
-#define __NR_wait4 114
-#define __NR_swapoff 115
-#define __NR_sysinfo 116
-#define __NR_ipc 117
-#define __NR_fsync 118
-#define __NR_sigreturn 119
-#define __NR_clone 120
-#define __NR_setdomainname 121
-#define __NR_uname 122
-#define __NR_adjtimex 124
-#define __NR_mprotect 125
-#define __NR_sigprocmask 126
-#define __NR_create_module 127
-#define __NR_init_module 128
-#define __NR_delete_module 129
-#define __NR_get_kernel_syms 130
-#define __NR_quotactl 131
-#define __NR_getpgid 132
-#define __NR_fchdir 133
-#define __NR_bdflush 134
-#define __NR_sysfs 135
-#define __NR_personality 136
-#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
-#define __NR_getdents 141
-#define __NR_flock 143
-#define __NR_msync 144
-#define __NR_readv 145
-#define __NR_writev 146
-#define __NR_getsid 147
-#define __NR_fdatasync 148
-#define __NR__sysctl 149
-#define __NR_mlock 150
-#define __NR_munlock 151
-#define __NR_mlockall 152
-#define __NR_munlockall 153
-#define __NR_sched_setparam 154
-#define __NR_sched_getparam 155
-#define __NR_sched_setscheduler 156
-#define __NR_sched_getscheduler 157
-#define __NR_sched_yield 158
-#define __NR_sched_get_priority_max 159
-#define __NR_sched_get_priority_min 160
-#define __NR_sched_rr_get_interval 161
-#define __NR_nanosleep 162
-#define __NR_mremap 163
-#define __NR_query_module 167
-#define __NR_poll 168
-#define __NR_nfsservctl 169
-#define __NR_prctl 172
-#define __NR_rt_sigreturn 173
-#define __NR_rt_sigaction 174
-#define __NR_rt_sigprocmask 175
-#define __NR_rt_sigpending 176
-#define __NR_rt_sigtimedwait 177
-#define __NR_rt_sigqueueinfo 178
-#define __NR_rt_sigsuspend 179
-#define __NR_pread64 180
-#define __NR_pwrite64 181
-#define __NR_getcwd 183
-#define __NR_capget 184
-#define __NR_capset 185
-#define __NR_sigaltstack 186
-#define __NR_sendfile 187
-#define __NR_getpmsg 188
-#define __NR_putpmsg 189
-#define __NR_vfork 190
-#define __NR_pivot_root 217
-#define __NR_mincore 218
-#define __NR_madvise 219
-#define __NR_getdents64 220
-#define __NR_readahead 222
-#define __NR_setxattr 224
-#define __NR_lsetxattr 225
-#define __NR_fsetxattr 226
-#define __NR_getxattr 227
-#define __NR_lgetxattr 228
-#define __NR_fgetxattr 229
-#define __NR_listxattr 230
-#define __NR_llistxattr 231
-#define __NR_flistxattr 232
-#define __NR_removexattr 233
-#define __NR_lremovexattr 234
-#define __NR_fremovexattr 235
-#define __NR_gettid 236
-#define __NR_tkill 237
-#define __NR_futex 238
-#define __NR_sched_setaffinity 239
-#define __NR_sched_getaffinity 240
-#define __NR_tgkill 241
-/* Number 242 is reserved for tux */
-#define __NR_io_setup 243
-#define __NR_io_destroy 244
-#define __NR_io_getevents 245
-#define __NR_io_submit 246
-#define __NR_io_cancel 247
-#define __NR_exit_group 248
-#define __NR_epoll_create 249
-#define __NR_epoll_ctl 250
-#define __NR_epoll_wait 251
-#define __NR_set_tid_address 252
-#define __NR_fadvise64 253
-#define __NR_timer_create 254
-#define __NR_timer_settime 255
-#define __NR_timer_gettime 256
-#define __NR_timer_getoverrun 257
-#define __NR_timer_delete 258
-#define __NR_clock_settime 259
-#define __NR_clock_gettime 260
-#define __NR_clock_getres 261
-#define __NR_clock_nanosleep 262
-/* Number 263 is reserved for vserver */
-#define __NR_statfs64 265
-#define __NR_fstatfs64 266
-#define __NR_remap_file_pages 267
-#define __NR_mbind 268
-#define __NR_get_mempolicy 269
-#define __NR_set_mempolicy 270
-#define __NR_mq_open 271
-#define __NR_mq_unlink 272
-#define __NR_mq_timedsend 273
-#define __NR_mq_timedreceive 274
-#define __NR_mq_notify 275
-#define __NR_mq_getsetattr 276
-#define __NR_kexec_load 277
-#define __NR_add_key 278
-#define __NR_request_key 279
-#define __NR_keyctl 280
-#define __NR_waitid 281
-#define __NR_ioprio_set 282
-#define __NR_ioprio_get 283
-#define __NR_inotify_init 284
-#define __NR_inotify_add_watch 285
-#define __NR_inotify_rm_watch 286
-#define __NR_migrate_pages 287
-#define __NR_openat 288
-#define __NR_mkdirat 289
-#define __NR_mknodat 290
-#define __NR_fchownat 291
-#define __NR_futimesat 292
-#define __NR_unlinkat 294
-#define __NR_renameat 295
-#define __NR_linkat 296
-#define __NR_symlinkat 297
-#define __NR_readlinkat 298
-#define __NR_fchmodat 299
-#define __NR_faccessat 300
-#define __NR_pselect6 301
-#define __NR_ppoll 302
-#define __NR_unshare 303
-#define __NR_set_robust_list 304
-#define __NR_get_robust_list 305
-#define __NR_splice 306
-#define __NR_sync_file_range 307
-#define __NR_tee 308
-#define __NR_vmsplice 309
-#define __NR_move_pages 310
-#define __NR_getcpu 311
-#define __NR_epoll_pwait 312
-#define __NR_utimes 313
-#define __NR_fallocate 314
-#define __NR_utimensat 315
-#define __NR_signalfd 316
-#define __NR_timerfd 317
-#define __NR_eventfd 318
-#define __NR_timerfd_create 319
-#define __NR_timerfd_settime 320
-#define __NR_timerfd_gettime 321
-#define __NR_signalfd4 322
-#define __NR_eventfd2 323
-#define __NR_inotify_init1 324
-#define __NR_pipe2 325
-#define __NR_dup3 326
-#define __NR_epoll_create1 327
-#define __NR_preadv 328
-#define __NR_pwritev 329
-#define __NR_rt_tgsigqueueinfo 330
-#define __NR_perf_event_open 331
-#define __NR_fanotify_init 332
-#define __NR_fanotify_mark 333
-#define __NR_prlimit64 334
-#define __NR_name_to_handle_at 335
-#define __NR_open_by_handle_at 336
-#define __NR_clock_adjtime 337
-#define __NR_syncfs 338
-#define __NR_setns 339
-#define __NR_process_vm_readv 340
-#define __NR_process_vm_writev 341
-#define __NR_s390_runtime_instr 342
-#define __NR_kcmp 343
-#define __NR_finit_module 344
-#define __NR_sched_setattr 345
-#define __NR_sched_getattr 346
-#define __NR_renameat2 347
-#define __NR_seccomp 348
-#define __NR_getrandom 349
-#define __NR_memfd_create 350
-#define __NR_bpf 351
-#define __NR_s390_pci_mmio_write 352
-#define __NR_s390_pci_mmio_read 353
-#define __NR_execveat 354
-#define __NR_userfaultfd 355
-#define __NR_membarrier 356
-#define __NR_recvmmsg 357
-#define __NR_sendmmsg 358
-#define __NR_socket 359
-#define __NR_socketpair 360
-#define __NR_bind 361
-#define __NR_connect 362
-#define __NR_listen 363
-#define __NR_accept4 364
-#define __NR_getsockopt 365
-#define __NR_setsockopt 366
-#define __NR_getsockname 367
-#define __NR_getpeername 368
-#define __NR_sendto 369
-#define __NR_sendmsg 370
-#define __NR_recvfrom 371
-#define __NR_recvmsg 372
-#define __NR_shutdown 373
-#define __NR_mlock2 374
-#define __NR_copy_file_range 375
-#define __NR_preadv2 376
-#define __NR_pwritev2 377
-#define __NR_s390_guarded_storage 378
-#define __NR_statx 379
-#define __NR_s390_sthyi 380
-#define NR_syscalls 381
-
-/*
- * There are some system calls that are not present on 64 bit, some
- * have a different name although they do the same (e.g. __NR_chown32
- * is __NR_chown on 64 bit).
- */
-#ifndef __s390x__
-
-#define __NR_time 13
-#define __NR_lchown 16
-#define __NR_setuid 23
-#define __NR_getuid 24
-#define __NR_stime 25
-#define __NR_setgid 46
-#define __NR_getgid 47
-#define __NR_geteuid 49
-#define __NR_getegid 50
-#define __NR_setreuid 70
-#define __NR_setregid 71
-#define __NR_getrlimit 76
-#define __NR_getgroups 80
-#define __NR_setgroups 81
-#define __NR_fchown 95
-#define __NR_ioperm 101
-#define __NR_setfsuid 138
-#define __NR_setfsgid 139
-#define __NR__llseek 140
-#define __NR__newselect 142
-#define __NR_setresuid 164
-#define __NR_getresuid 165
-#define __NR_setresgid 170
-#define __NR_getresgid 171
-#define __NR_chown 182
-#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
-#define __NR_mmap2 192
-#define __NR_truncate64 193
-#define __NR_ftruncate64 194
-#define __NR_stat64 195
-#define __NR_lstat64 196
-#define __NR_fstat64 197
-#define __NR_lchown32 198
-#define __NR_getuid32 199
-#define __NR_getgid32 200
-#define __NR_geteuid32 201
-#define __NR_getegid32 202
-#define __NR_setreuid32 203
-#define __NR_setregid32 204
-#define __NR_getgroups32 205
-#define __NR_setgroups32 206
-#define __NR_fchown32 207
-#define __NR_setresuid32 208
-#define __NR_getresuid32 209
-#define __NR_setresgid32 210
-#define __NR_getresgid32 211
-#define __NR_chown32 212
-#define __NR_setuid32 213
-#define __NR_setgid32 214
-#define __NR_setfsuid32 215
-#define __NR_setfsgid32 216
-#define __NR_fcntl64 221
-#define __NR_sendfile64 223
-#define __NR_fadvise64_64 264
-#define __NR_fstatat64 293
-
+#ifdef __s390x__
+#include <asm/unistd_64.h>
#else
-
-#define __NR_select 142
-#define __NR_getrlimit 191 /* SuS compliant getrlimit */
-#define __NR_lchown 198
-#define __NR_getuid 199
-#define __NR_getgid 200
-#define __NR_geteuid 201
-#define __NR_getegid 202
-#define __NR_setreuid 203
-#define __NR_setregid 204
-#define __NR_getgroups 205
-#define __NR_setgroups 206
-#define __NR_fchown 207
-#define __NR_setresuid 208
-#define __NR_getresuid 209
-#define __NR_setresgid 210
-#define __NR_getresgid 211
-#define __NR_chown 212
-#define __NR_setuid 213
-#define __NR_setgid 214
-#define __NR_setfsuid 215
-#define __NR_setfsgid 216
-#define __NR_newfstatat 293
-
+#include <asm/unistd_32.h>
#endif
#endif /* _ASM_S390_UNISTD_H_ */
diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h
new file mode 100644
index 0000000000..1ae66a263b
--- /dev/null
+++ b/linux-headers/asm-s390/unistd_32.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_S390_UNISTD_32_H
+#define _ASM_S390_UNISTD_32_H
+
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_restart_syscall 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lchown 16
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_setpgid 57
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_symlink 83
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_lookup_dcookie 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_setresuid 164
+#define __NR_getresuid 165
+#define __NR_query_module 167
+#define __NR_poll 168
+#define __NR_nfsservctl 169
+#define __NR_setresgid 170
+#define __NR_getresgid 171
+#define __NR_prctl 172
+#define __NR_rt_sigreturn 173
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigtimedwait 177
+#define __NR_rt_sigqueueinfo 178
+#define __NR_rt_sigsuspend 179
+#define __NR_pread64 180
+#define __NR_pwrite64 181
+#define __NR_chown 182
+#define __NR_getcwd 183
+#define __NR_capget 184
+#define __NR_capset 185
+#define __NR_sigaltstack 186
+#define __NR_sendfile 187
+#define __NR_getpmsg 188
+#define __NR_putpmsg 189
+#define __NR_vfork 190
+#define __NR_ugetrlimit 191
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
+#define __NR_lchown32 198
+#define __NR_getuid32 199
+#define __NR_getgid32 200
+#define __NR_geteuid32 201
+#define __NR_getegid32 202
+#define __NR_setreuid32 203
+#define __NR_setregid32 204
+#define __NR_getgroups32 205
+#define __NR_setgroups32 206
+#define __NR_fchown32 207
+#define __NR_setresuid32 208
+#define __NR_getresuid32 209
+#define __NR_setresgid32 210
+#define __NR_getresgid32 211
+#define __NR_chown32 212
+#define __NR_setuid32 213
+#define __NR_setgid32 214
+#define __NR_setfsuid32 215
+#define __NR_setfsgid32 216
+#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_getdents64 220
+#define __NR_fcntl64 221
+#define __NR_readahead 222
+#define __NR_sendfile64 223
+#define __NR_setxattr 224
+#define __NR_lsetxattr 225
+#define __NR_fsetxattr 226
+#define __NR_getxattr 227
+#define __NR_lgetxattr 228
+#define __NR_fgetxattr 229
+#define __NR_listxattr 230
+#define __NR_llistxattr 231
+#define __NR_flistxattr 232
+#define __NR_removexattr 233
+#define __NR_lremovexattr 234
+#define __NR_fremovexattr 235
+#define __NR_gettid 236
+#define __NR_tkill 237
+#define __NR_futex 238
+#define __NR_sched_setaffinity 239
+#define __NR_sched_getaffinity 240
+#define __NR_tgkill 241
+#define __NR_io_setup 243
+#define __NR_io_destroy 244
+#define __NR_io_getevents 245
+#define __NR_io_submit 246
+#define __NR_io_cancel 247
+#define __NR_exit_group 248
+#define __NR_epoll_create 249
+#define __NR_epoll_ctl 250
+#define __NR_epoll_wait 251
+#define __NR_set_tid_address 252
+#define __NR_fadvise64 253
+#define __NR_timer_create 254
+#define __NR_timer_settime 255
+#define __NR_timer_gettime 256
+#define __NR_timer_getoverrun 257
+#define __NR_timer_delete 258
+#define __NR_clock_settime 259
+#define __NR_clock_gettime 260
+#define __NR_clock_getres 261
+#define __NR_clock_nanosleep 262
+#define __NR_fadvise64_64 264
+#define __NR_statfs64 265
+#define __NR_fstatfs64 266
+#define __NR_remap_file_pages 267
+#define __NR_mbind 268
+#define __NR_get_mempolicy 269
+#define __NR_set_mempolicy 270
+#define __NR_mq_open 271
+#define __NR_mq_unlink 272
+#define __NR_mq_timedsend 273
+#define __NR_mq_timedreceive 274
+#define __NR_mq_notify 275
+#define __NR_mq_getsetattr 276
+#define __NR_kexec_load 277
+#define __NR_add_key 278
+#define __NR_request_key 279
+#define __NR_keyctl 280
+#define __NR_waitid 281
+#define __NR_ioprio_set 282
+#define __NR_ioprio_get 283
+#define __NR_inotify_init 284
+#define __NR_inotify_add_watch 285
+#define __NR_inotify_rm_watch 286
+#define __NR_migrate_pages 287
+#define __NR_openat 288
+#define __NR_mkdirat 289
+#define __NR_mknodat 290
+#define __NR_fchownat 291
+#define __NR_futimesat 292
+#define __NR_fstatat64 293
+#define __NR_unlinkat 294
+#define __NR_renameat 295
+#define __NR_linkat 296
+#define __NR_symlinkat 297
+#define __NR_readlinkat 298
+#define __NR_fchmodat 299
+#define __NR_faccessat 300
+#define __NR_pselect6 301
+#define __NR_ppoll 302
+#define __NR_unshare 303
+#define __NR_set_robust_list 304
+#define __NR_get_robust_list 305
+#define __NR_splice 306
+#define __NR_sync_file_range 307
+#define __NR_tee 308
+#define __NR_vmsplice 309
+#define __NR_move_pages 310
+#define __NR_getcpu 311
+#define __NR_epoll_pwait 312
+#define __NR_utimes 313
+#define __NR_fallocate 314
+#define __NR_utimensat 315
+#define __NR_signalfd 316
+#define __NR_timerfd 317
+#define __NR_eventfd 318
+#define __NR_timerfd_create 319
+#define __NR_timerfd_settime 320
+#define __NR_timerfd_gettime 321
+#define __NR_signalfd4 322
+#define __NR_eventfd2 323
+#define __NR_inotify_init1 324
+#define __NR_pipe2 325
+#define __NR_dup3 326
+#define __NR_epoll_create1 327
+#define __NR_preadv 328
+#define __NR_pwritev 329
+#define __NR_rt_tgsigqueueinfo 330
+#define __NR_perf_event_open 331
+#define __NR_fanotify_init 332
+#define __NR_fanotify_mark 333
+#define __NR_prlimit64 334
+#define __NR_name_to_handle_at 335
+#define __NR_open_by_handle_at 336
+#define __NR_clock_adjtime 337
+#define __NR_syncfs 338
+#define __NR_setns 339
+#define __NR_process_vm_readv 340
+#define __NR_process_vm_writev 341
+#define __NR_s390_runtime_instr 342
+#define __NR_kcmp 343
+#define __NR_finit_module 344
+#define __NR_sched_setattr 345
+#define __NR_sched_getattr 346
+#define __NR_renameat2 347
+#define __NR_seccomp 348
+#define __NR_getrandom 349
+#define __NR_memfd_create 350
+#define __NR_bpf 351
+#define __NR_s390_pci_mmio_write 352
+#define __NR_s390_pci_mmio_read 353
+#define __NR_execveat 354
+#define __NR_userfaultfd 355
+#define __NR_membarrier 356
+#define __NR_recvmmsg 357
+#define __NR_sendmmsg 358
+#define __NR_socket 359
+#define __NR_socketpair 360
+#define __NR_bind 361
+#define __NR_connect 362
+#define __NR_listen 363
+#define __NR_accept4 364
+#define __NR_getsockopt 365
+#define __NR_setsockopt 366
+#define __NR_getsockname 367
+#define __NR_getpeername 368
+#define __NR_sendto 369
+#define __NR_sendmsg 370
+#define __NR_recvfrom 371
+#define __NR_recvmsg 372
+#define __NR_shutdown 373
+#define __NR_mlock2 374
+#define __NR_copy_file_range 375
+#define __NR_preadv2 376
+#define __NR_pwritev2 377
+#define __NR_s390_guarded_storage 378
+#define __NR_statx 379
+#define __NR_s390_sthyi 380
+
+#endif /* _ASM_S390_UNISTD_32_H */
diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h
new file mode 100644
index 0000000000..8aa9d046a9
--- /dev/null
+++ b/linux-headers/asm-s390/unistd_64.h
@@ -0,0 +1,331 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_S390_UNISTD_64_H
+#define _ASM_S390_UNISTD_64_H
+
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_restart_syscall 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_brk 45
+#define __NR_signal 48
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_setpgid 57
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_symlink 83
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_lookup_dcookie 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137
+#define __NR_getdents 141
+#define __NR_select 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_query_module 167
+#define __NR_poll 168
+#define __NR_nfsservctl 169
+#define __NR_prctl 172
+#define __NR_rt_sigreturn 173
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigtimedwait 177
+#define __NR_rt_sigqueueinfo 178
+#define __NR_rt_sigsuspend 179
+#define __NR_pread64 180
+#define __NR_pwrite64 181
+#define __NR_getcwd 183
+#define __NR_capget 184
+#define __NR_capset 185
+#define __NR_sigaltstack 186
+#define __NR_sendfile 187
+#define __NR_getpmsg 188
+#define __NR_putpmsg 189
+#define __NR_vfork 190
+#define __NR_getrlimit 191
+#define __NR_lchown 198
+#define __NR_getuid 199
+#define __NR_getgid 200
+#define __NR_geteuid 201
+#define __NR_getegid 202
+#define __NR_setreuid 203
+#define __NR_setregid 204
+#define __NR_getgroups 205
+#define __NR_setgroups 206
+#define __NR_fchown 207
+#define __NR_setresuid 208
+#define __NR_getresuid 209
+#define __NR_setresgid 210
+#define __NR_getresgid 211
+#define __NR_chown 212
+#define __NR_setuid 213
+#define __NR_setgid 214
+#define __NR_setfsuid 215
+#define __NR_setfsgid 216
+#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+#define __NR_getdents64 220
+#define __NR_readahead 222
+#define __NR_setxattr 224
+#define __NR_lsetxattr 225
+#define __NR_fsetxattr 226
+#define __NR_getxattr 227
+#define __NR_lgetxattr 228
+#define __NR_fgetxattr 229
+#define __NR_listxattr 230
+#define __NR_llistxattr 231
+#define __NR_flistxattr 232
+#define __NR_removexattr 233
+#define __NR_lremovexattr 234
+#define __NR_fremovexattr 235
+#define __NR_gettid 236
+#define __NR_tkill 237
+#define __NR_futex 238
+#define __NR_sched_setaffinity 239
+#define __NR_sched_getaffinity 240
+#define __NR_tgkill 241
+#define __NR_io_setup 243
+#define __NR_io_destroy 244
+#define __NR_io_getevents 245
+#define __NR_io_submit 246
+#define __NR_io_cancel 247
+#define __NR_exit_group 248
+#define __NR_epoll_create 249
+#define __NR_epoll_ctl 250
+#define __NR_epoll_wait 251
+#define __NR_set_tid_address 252
+#define __NR_fadvise64 253
+#define __NR_timer_create 254
+#define __NR_timer_settime 255
+#define __NR_timer_gettime 256
+#define __NR_timer_getoverrun 257
+#define __NR_timer_delete 258
+#define __NR_clock_settime 259
+#define __NR_clock_gettime 260
+#define __NR_clock_getres 261
+#define __NR_clock_nanosleep 262
+#define __NR_statfs64 265
+#define __NR_fstatfs64 266
+#define __NR_remap_file_pages 267
+#define __NR_mbind 268
+#define __NR_get_mempolicy 269
+#define __NR_set_mempolicy 270
+#define __NR_mq_open 271
+#define __NR_mq_unlink 272
+#define __NR_mq_timedsend 273
+#define __NR_mq_timedreceive 274
+#define __NR_mq_notify 275
+#define __NR_mq_getsetattr 276
+#define __NR_kexec_load 277
+#define __NR_add_key 278
+#define __NR_request_key 279
+#define __NR_keyctl 280
+#define __NR_waitid 281
+#define __NR_ioprio_set 282
+#define __NR_ioprio_get 283
+#define __NR_inotify_init 284
+#define __NR_inotify_add_watch 285
+#define __NR_inotify_rm_watch 286
+#define __NR_migrate_pages 287
+#define __NR_openat 288
+#define __NR_mkdirat 289
+#define __NR_mknodat 290
+#define __NR_fchownat 291
+#define __NR_futimesat 292
+#define __NR_newfstatat 293
+#define __NR_unlinkat 294
+#define __NR_renameat 295
+#define __NR_linkat 296
+#define __NR_symlinkat 297
+#define __NR_readlinkat 298
+#define __NR_fchmodat 299
+#define __NR_faccessat 300
+#define __NR_pselect6 301
+#define __NR_ppoll 302
+#define __NR_unshare 303
+#define __NR_set_robust_list 304
+#define __NR_get_robust_list 305
+#define __NR_splice 306
+#define __NR_sync_file_range 307
+#define __NR_tee 308
+#define __NR_vmsplice 309
+#define __NR_move_pages 310
+#define __NR_getcpu 311
+#define __NR_epoll_pwait 312
+#define __NR_utimes 313
+#define __NR_fallocate 314
+#define __NR_utimensat 315
+#define __NR_signalfd 316
+#define __NR_timerfd 317
+#define __NR_eventfd 318
+#define __NR_timerfd_create 319
+#define __NR_timerfd_settime 320
+#define __NR_timerfd_gettime 321
+#define __NR_signalfd4 322
+#define __NR_eventfd2 323
+#define __NR_inotify_init1 324
+#define __NR_pipe2 325
+#define __NR_dup3 326
+#define __NR_epoll_create1 327
+#define __NR_preadv 328
+#define __NR_pwritev 329
+#define __NR_rt_tgsigqueueinfo 330
+#define __NR_perf_event_open 331
+#define __NR_fanotify_init 332
+#define __NR_fanotify_mark 333
+#define __NR_prlimit64 334
+#define __NR_name_to_handle_at 335
+#define __NR_open_by_handle_at 336
+#define __NR_clock_adjtime 337
+#define __NR_syncfs 338
+#define __NR_setns 339
+#define __NR_process_vm_readv 340
+#define __NR_process_vm_writev 341
+#define __NR_s390_runtime_instr 342
+#define __NR_kcmp 343
+#define __NR_finit_module 344
+#define __NR_sched_setattr 345
+#define __NR_sched_getattr 346
+#define __NR_renameat2 347
+#define __NR_seccomp 348
+#define __NR_getrandom 349
+#define __NR_memfd_create 350
+#define __NR_bpf 351
+#define __NR_s390_pci_mmio_write 352
+#define __NR_s390_pci_mmio_read 353
+#define __NR_execveat 354
+#define __NR_userfaultfd 355
+#define __NR_membarrier 356
+#define __NR_recvmmsg 357
+#define __NR_sendmmsg 358
+#define __NR_socket 359
+#define __NR_socketpair 360
+#define __NR_bind 361
+#define __NR_connect 362
+#define __NR_listen 363
+#define __NR_accept4 364
+#define __NR_getsockopt 365
+#define __NR_setsockopt 366
+#define __NR_getsockname 367
+#define __NR_getpeername 368
+#define __NR_sendto 369
+#define __NR_sendmsg 370
+#define __NR_recvfrom 371
+#define __NR_recvmsg 372
+#define __NR_shutdown 373
+#define __NR_mlock2 374
+#define __NR_copy_file_range 375
+#define __NR_preadv2 376
+#define __NR_pwritev2 377
+#define __NR_s390_guarded_storage 378
+#define __NR_statx 379
+#define __NR_s390_sthyi 380
+
+#endif /* _ASM_S390_UNISTD_64_H */
diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h
index 4c300f6aaa..4c58184395 100644
--- a/linux-headers/asm-x86/kvm_para.h
+++ b/linux-headers/asm-x86/kvm_para.h
@@ -25,6 +25,8 @@
#define KVM_FEATURE_STEAL_TIME 5
#define KVM_FEATURE_PV_EOI 6
#define KVM_FEATURE_PV_UNHALT 7
+#define KVM_FEATURE_PV_TLB_FLUSH 9
+#define KVM_FEATURE_ASYNC_PF_VMEXIT 10
/* The last 8 bits are used to indicate how to interpret the flags field
* in pvclock structure. If no bits are set, all flags are ignored.
@@ -51,6 +53,9 @@ struct kvm_steal_time {
__u32 pad[11];
};
+#define KVM_VCPU_PREEMPTED (1 << 0)
+#define KVM_VCPU_FLUSH_TLB (1 << 1)
+
#define KVM_CLOCK_PAIRING_WALLCLOCK 0
struct kvm_clock_pairing {
__s64 sec;
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index d92c9b2f0e..a167be89d1 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -761,6 +761,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07
#define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08
#define KVM_GET_EMULATED_CPUID _IOWR(KVMIO, 0x09, struct kvm_cpuid2)
+#define KVM_GET_MSR_FEATURE_INDEX_LIST _IOWR(KVMIO, 0x0a, struct kvm_msr_list)
/*
* Extension capability list.
@@ -934,6 +935,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_S390_AIS_MIGRATION 150
#define KVM_CAP_PPC_GET_CPU_CHAR 151
#define KVM_CAP_S390_BPB 152
+#define KVM_CAP_GET_MSR_FEATURES 153
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1362,6 +1364,96 @@ struct kvm_s390_ucas_mapping {
/* Available with KVM_CAP_S390_CMMA_MIGRATION */
#define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log)
#define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
+/* Memory Encryption Commands */
+#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long)
+
+struct kvm_enc_region {
+ __u64 addr;
+ __u64 size;
+};
+
+#define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region)
+#define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region)
+
+/* Secure Encrypted Virtualization command */
+enum sev_cmd_id {
+ /* Guest initialization commands */
+ KVM_SEV_INIT = 0,
+ KVM_SEV_ES_INIT,
+ /* Guest launch commands */
+ KVM_SEV_LAUNCH_START,
+ KVM_SEV_LAUNCH_UPDATE_DATA,
+ KVM_SEV_LAUNCH_UPDATE_VMSA,
+ KVM_SEV_LAUNCH_SECRET,
+ KVM_SEV_LAUNCH_MEASURE,
+ KVM_SEV_LAUNCH_FINISH,
+ /* Guest migration commands (outgoing) */
+ KVM_SEV_SEND_START,
+ KVM_SEV_SEND_UPDATE_DATA,
+ KVM_SEV_SEND_UPDATE_VMSA,
+ KVM_SEV_SEND_FINISH,
+ /* Guest migration commands (incoming) */
+ KVM_SEV_RECEIVE_START,
+ KVM_SEV_RECEIVE_UPDATE_DATA,
+ KVM_SEV_RECEIVE_UPDATE_VMSA,
+ KVM_SEV_RECEIVE_FINISH,
+ /* Guest status and debug commands */
+ KVM_SEV_GUEST_STATUS,
+ KVM_SEV_DBG_DECRYPT,
+ KVM_SEV_DBG_ENCRYPT,
+ /* Guest certificates commands */
+ KVM_SEV_CERT_EXPORT,
+
+ KVM_SEV_NR_MAX,
+};
+
+struct kvm_sev_cmd {
+ __u32 id;
+ __u64 data;
+ __u32 error;
+ __u32 sev_fd;
+};
+
+struct kvm_sev_launch_start {
+ __u32 handle;
+ __u32 policy;
+ __u64 dh_uaddr;
+ __u32 dh_len;
+ __u64 session_uaddr;
+ __u32 session_len;
+};
+
+struct kvm_sev_launch_update_data {
+ __u64 uaddr;
+ __u32 len;
+};
+
+
+struct kvm_sev_launch_secret {
+ __u64 hdr_uaddr;
+ __u32 hdr_len;
+ __u64 guest_uaddr;
+ __u32 guest_len;
+ __u64 trans_uaddr;
+ __u32 trans_len;
+};
+
+struct kvm_sev_launch_measure {
+ __u64 uaddr;
+ __u32 len;
+};
+
+struct kvm_sev_guest_status {
+ __u32 handle;
+ __u32 policy;
+ __u32 state;
+};
+
+struct kvm_sev_dbg {
+ __u64 src_uaddr;
+ __u64 dst_uaddr;
+ __u32 len;
+};
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
diff --git a/linux-headers/linux/psci.h b/linux-headers/linux/psci.h
index ccd17731c6..3905492d18 100644
--- a/linux-headers/linux/psci.h
+++ b/linux-headers/linux/psci.h
@@ -88,6 +88,9 @@
(((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
#define PSCI_VERSION_MINOR(ver) \
((ver) & PSCI_VERSION_MINOR_MASK)
+#define PSCI_VERSION(maj, min) \
+ ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | \
+ ((min) & PSCI_VERSION_MINOR_MASK))
/* PSCI features decoding (>=1.0) */
#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1
diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h
new file mode 100644
index 0000000000..33e247471a
--- /dev/null
+++ b/linux-headers/linux/psp-sev.h
@@ -0,0 +1,142 @@
+/*
+ * Userspace interface for AMD Secure Encrypted Virtualization (SEV)
+ * platform management commands.
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * SEV spec 0.14 is available at:
+ * http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PSP_SEV_USER_H__
+#define __PSP_SEV_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * SEV platform commands
+ */
+enum {
+ SEV_FACTORY_RESET = 0,
+ SEV_PLATFORM_STATUS,
+ SEV_PEK_GEN,
+ SEV_PEK_CSR,
+ SEV_PDH_GEN,
+ SEV_PDH_CERT_EXPORT,
+ SEV_PEK_CERT_IMPORT,
+
+ SEV_MAX,
+};
+
+/**
+ * SEV Firmware status code
+ */
+typedef enum {
+ SEV_RET_SUCCESS = 0,
+ SEV_RET_INVALID_PLATFORM_STATE,
+ SEV_RET_INVALID_GUEST_STATE,
+ SEV_RET_INAVLID_CONFIG,
+ SEV_RET_INVALID_LEN,
+ SEV_RET_ALREADY_OWNED,
+ SEV_RET_INVALID_CERTIFICATE,
+ SEV_RET_POLICY_FAILURE,
+ SEV_RET_INACTIVE,
+ SEV_RET_INVALID_ADDRESS,
+ SEV_RET_BAD_SIGNATURE,
+ SEV_RET_BAD_MEASUREMENT,
+ SEV_RET_ASID_OWNED,
+ SEV_RET_INVALID_ASID,
+ SEV_RET_WBINVD_REQUIRED,
+ SEV_RET_DFFLUSH_REQUIRED,
+ SEV_RET_INVALID_GUEST,
+ SEV_RET_INVALID_COMMAND,
+ SEV_RET_ACTIVE,
+ SEV_RET_HWSEV_RET_PLATFORM,
+ SEV_RET_HWSEV_RET_UNSAFE,
+ SEV_RET_UNSUPPORTED,
+ SEV_RET_MAX,
+} sev_ret_code;
+
+/**
+ * struct sev_user_data_status - PLATFORM_STATUS command parameters
+ *
+ * @major: major API version
+ * @minor: minor API version
+ * @state: platform state
+ * @flags: platform config flags
+ * @build: firmware build id for API version
+ * @guest_count: number of active guests
+ */
+struct sev_user_data_status {
+ __u8 api_major; /* Out */
+ __u8 api_minor; /* Out */
+ __u8 state; /* Out */
+ __u32 flags; /* Out */
+ __u8 build; /* Out */
+ __u32 guest_count; /* Out */
+} __attribute__((packed));
+
+/**
+ * struct sev_user_data_pek_csr - PEK_CSR command parameters
+ *
+ * @address: PEK certificate chain
+ * @length: length of certificate
+ */
+struct sev_user_data_pek_csr {
+ __u64 address; /* In */
+ __u32 length; /* In/Out */
+} __attribute__((packed));
+
+/**
+ * struct sev_user_data_cert_import - PEK_CERT_IMPORT command parameters
+ *
+ * @pek_address: PEK certificate chain
+ * @pek_len: length of PEK certificate
+ * @oca_address: OCA certificate chain
+ * @oca_len: length of OCA certificate
+ */
+struct sev_user_data_pek_cert_import {
+ __u64 pek_cert_address; /* In */
+ __u32 pek_cert_len; /* In */
+ __u64 oca_cert_address; /* In */
+ __u32 oca_cert_len; /* In */
+} __attribute__((packed));
+
+/**
+ * struct sev_user_data_pdh_cert_export - PDH_CERT_EXPORT command parameters
+ *
+ * @pdh_address: PDH certificate address
+ * @pdh_len: length of PDH certificate
+ * @cert_chain_address: PDH certificate chain
+ * @cert_chain_len: length of PDH certificate chain
+ */
+struct sev_user_data_pdh_cert_export {
+ __u64 pdh_cert_address; /* In */
+ __u32 pdh_cert_len; /* In/Out */
+ __u64 cert_chain_address; /* In */
+ __u32 cert_chain_len; /* In/Out */
+} __attribute__((packed));
+
+/**
+ * struct sev_issue_cmd - SEV ioctl parameters
+ *
+ * @cmd: SEV commands to execute
+ * @opaque: pointer to the command structure
+ * @error: SEV FW return code on failure
+ */
+struct sev_issue_cmd {
+ __u32 cmd; /* In */
+ __u64 data; /* In */
+ __u32 error; /* Out */
+} __attribute__((packed));
+
+#define SEV_IOC_TYPE 'S'
+#define SEV_ISSUE_CMD _IOWR(SEV_IOC_TYPE, 0x0, struct sev_issue_cmd)
+
+#endif /* __PSP_USER_SEV_H */
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
index 4312e961ff..3a0a305c8c 100644
--- a/linux-headers/linux/vfio.h
+++ b/linux-headers/linux/vfio.h
@@ -301,6 +301,16 @@ struct vfio_region_info_cap_type {
#define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2)
#define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3)
+/*
+ * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped
+ * which allows direct access to non-MSIX registers which happened to be within
+ * the same system page.
+ *
+ * Even though the userspace gets direct access to the MSIX data, the existing
+ * VFIO_DEVICE_SET_IRQS interface must still be used for MSIX configuration.
+ */
+#define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE 3
+
/**
* VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
* struct vfio_irq_info)
@@ -503,6 +513,68 @@ struct vfio_pci_hot_reset {
#define VFIO_DEVICE_PCI_HOT_RESET _IO(VFIO_TYPE, VFIO_BASE + 13)
+/**
+ * VFIO_DEVICE_QUERY_GFX_PLANE - _IOW(VFIO_TYPE, VFIO_BASE + 14,
+ * struct vfio_device_query_gfx_plane)
+ *
+ * Set the drm_plane_type and flags, then retrieve the gfx plane info.
+ *
+ * flags supported:
+ * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_DMABUF are set
+ * to ask if the mdev supports dma-buf. 0 on support, -EINVAL on no
+ * support for dma-buf.
+ * - VFIO_GFX_PLANE_TYPE_PROBE and VFIO_GFX_PLANE_TYPE_REGION are set
+ * to ask if the mdev supports region. 0 on support, -EINVAL on no
+ * support for region.
+ * - VFIO_GFX_PLANE_TYPE_DMABUF or VFIO_GFX_PLANE_TYPE_REGION is set
+ * with each call to query the plane info.
+ * - Others are invalid and return -EINVAL.
+ *
+ * Note:
+ * 1. Plane could be disabled by guest. In that case, success will be
+ * returned with zero-initialized drm_format, size, width and height
+ * fields.
+ * 2. x_hot/y_hot is set to 0xFFFFFFFF if no hotspot information available
+ *
+ * Return: 0 on success, -errno on other failure.
+ */
+struct vfio_device_gfx_plane_info {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_GFX_PLANE_TYPE_PROBE (1 << 0)
+#define VFIO_GFX_PLANE_TYPE_DMABUF (1 << 1)
+#define VFIO_GFX_PLANE_TYPE_REGION (1 << 2)
+ /* in */
+ __u32 drm_plane_type; /* type of plane: DRM_PLANE_TYPE_* */
+ /* out */
+ __u32 drm_format; /* drm format of plane */
+ __u64 drm_format_mod; /* tiled mode */
+ __u32 width; /* width of plane */
+ __u32 height; /* height of plane */
+ __u32 stride; /* stride of plane */
+ __u32 size; /* size of plane in bytes, align on page*/
+ __u32 x_pos; /* horizontal position of cursor plane */
+ __u32 y_pos; /* vertical position of cursor plane*/
+ __u32 x_hot; /* horizontal position of cursor hotspot */
+ __u32 y_hot; /* vertical position of cursor hotspot */
+ union {
+ __u32 region_index; /* region index */
+ __u32 dmabuf_id; /* dma-buf id */
+ };
+};
+
+#define VFIO_DEVICE_QUERY_GFX_PLANE _IO(VFIO_TYPE, VFIO_BASE + 14)
+
+/**
+ * VFIO_DEVICE_GET_GFX_DMABUF - _IOW(VFIO_TYPE, VFIO_BASE + 15, __u32)
+ *
+ * Return a new dma-buf file descriptor for an exposed guest framebuffer
+ * described by the provided dmabuf_id. The dmabuf_id is returned from VFIO_
+ * DEVICE_QUERY_GFX_PLANE as a token of the exposed guest framebuffer.
+ */
+
+#define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15)
+
/* -------- API for Type1 VFIO IOMMU -------- */
/**
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 5fc130cc20..4563a3190b 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -354,7 +354,6 @@ enum {
/* The commpage only exists for 32 bit kernels */
-#define TARGET_HAS_VALIDATE_GUEST_SPACE
/* Return 1 if the proposed guest space is suitable for the guest.
* Return 0 if the proposed guest space isn't suitable, but another
* address space should be tried.
@@ -363,8 +362,8 @@ enum {
* The guest code may leave a page mapped and populate it if the
* address is suitable.
*/
-static int validate_guest_space(unsigned long guest_base,
- unsigned long guest_size)
+static int init_guest_commpage(unsigned long guest_base,
+ unsigned long guest_size)
{
unsigned long real_start, test_page_addr;
@@ -375,6 +374,11 @@ static int validate_guest_space(unsigned long guest_base,
/* If the commpage lies within the already allocated guest space,
* then there is no way we can allocate it.
+ *
+ * You may be thinking that that this check is redundant because
+ * we already validated the guest size against MAX_RESERVED_VA;
+ * but if qemu_host_page_mask is unusually large, then
+ * test_page_addr may be lower.
*/
if (test_page_addr >= guest_base
&& test_page_addr < (guest_base + guest_size)) {
@@ -563,78 +567,6 @@ static uint32_t get_elf_hwcap(void)
#endif /* not TARGET_AARCH64 */
#endif /* TARGET_ARM */
-#ifdef TARGET_UNICORE32
-
-#define ELF_START_MMAP 0x80000000
-
-#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2LSB
-#define ELF_ARCH EM_UNICORE32
-
-static inline void init_thread(struct target_pt_regs *regs,
- struct image_info *infop)
-{
- abi_long stack = infop->start_stack;
- memset(regs, 0, sizeof(*regs));
- regs->UC32_REG_asr = 0x10;
- regs->UC32_REG_pc = infop->entry & 0xfffffffe;
- regs->UC32_REG_sp = infop->start_stack;
- /* FIXME - what to for failure of get_user()? */
- get_user_ual(regs->UC32_REG_02, stack + 8); /* envp */
- get_user_ual(regs->UC32_REG_01, stack + 4); /* envp */
- /* XXX: it seems that r0 is zeroed after ! */
- regs->UC32_REG_00 = 0;
-}
-
-#define ELF_NREG 34
-typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
-
-static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUUniCore32State *env)
-{
- (*regs)[0] = env->regs[0];
- (*regs)[1] = env->regs[1];
- (*regs)[2] = env->regs[2];
- (*regs)[3] = env->regs[3];
- (*regs)[4] = env->regs[4];
- (*regs)[5] = env->regs[5];
- (*regs)[6] = env->regs[6];
- (*regs)[7] = env->regs[7];
- (*regs)[8] = env->regs[8];
- (*regs)[9] = env->regs[9];
- (*regs)[10] = env->regs[10];
- (*regs)[11] = env->regs[11];
- (*regs)[12] = env->regs[12];
- (*regs)[13] = env->regs[13];
- (*regs)[14] = env->regs[14];
- (*regs)[15] = env->regs[15];
- (*regs)[16] = env->regs[16];
- (*regs)[17] = env->regs[17];
- (*regs)[18] = env->regs[18];
- (*regs)[19] = env->regs[19];
- (*regs)[20] = env->regs[20];
- (*regs)[21] = env->regs[21];
- (*regs)[22] = env->regs[22];
- (*regs)[23] = env->regs[23];
- (*regs)[24] = env->regs[24];
- (*regs)[25] = env->regs[25];
- (*regs)[26] = env->regs[26];
- (*regs)[27] = env->regs[27];
- (*regs)[28] = env->regs[28];
- (*regs)[29] = env->regs[29];
- (*regs)[30] = env->regs[30];
- (*regs)[31] = env->regs[31];
-
- (*regs)[32] = cpu_asr_read((CPUUniCore32State *)env);
- (*regs)[33] = env->regs[0]; /* XXX */
-}
-
-#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 4096
-
-#define ELF_HWCAP (UC32_HWCAP_CMOV | UC32_HWCAP_UCF64)
-
-#endif
-
#ifdef TARGET_SPARC
#ifdef TARGET_SPARC64
@@ -1341,6 +1273,64 @@ static inline void init_thread(struct target_pt_regs *regs,
#endif /* TARGET_HPPA */
+#ifdef TARGET_XTENSA
+
+#define ELF_START_MMAP 0x20000000
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_XTENSA
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->windowbase = 0;
+ regs->windowstart = 1;
+ regs->areg[1] = infop->start_stack;
+ regs->pc = infop->entry;
+}
+
+/* See linux kernel: arch/xtensa/include/asm/elf.h. */
+#define ELF_NREG 128
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+enum {
+ TARGET_REG_PC,
+ TARGET_REG_PS,
+ TARGET_REG_LBEG,
+ TARGET_REG_LEND,
+ TARGET_REG_LCOUNT,
+ TARGET_REG_SAR,
+ TARGET_REG_WINDOWSTART,
+ TARGET_REG_WINDOWBASE,
+ TARGET_REG_THREADPTR,
+ TARGET_REG_AR0 = 64,
+};
+
+static void elf_core_copy_regs(target_elf_gregset_t *regs,
+ const CPUXtensaState *env)
+{
+ unsigned i;
+
+ (*regs)[TARGET_REG_PC] = tswapreg(env->pc);
+ (*regs)[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM);
+ (*regs)[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]);
+ (*regs)[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]);
+ (*regs)[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]);
+ (*regs)[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]);
+ (*regs)[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]);
+ (*regs)[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]);
+ (*regs)[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]);
+ xtensa_sync_phys_from_window((CPUXtensaState *)env);
+ for (i = 0; i < env->config->nareg; ++i) {
+ (*regs)[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]);
+ }
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif /* TARGET_XTENSA */
+
#ifndef ELF_PLATFORM
#define ELF_PLATFORM (NULL)
#endif
@@ -1869,21 +1859,12 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
return sp;
}
-#ifndef TARGET_HAS_VALIDATE_GUEST_SPACE
-/* If the guest doesn't have a validation function just agree */
-static int validate_guest_space(unsigned long guest_base,
- unsigned long guest_size)
-{
- return 1;
-}
-#endif
-
unsigned long init_guest_space(unsigned long host_start,
unsigned long host_size,
unsigned long guest_start,
bool fixed)
{
- unsigned long current_start, real_start;
+ unsigned long current_start, aligned_start;
int flags;
assert(host_start || host_size);
@@ -1891,11 +1872,12 @@ unsigned long init_guest_space(unsigned long host_start,
/* If just a starting address is given, then just verify that
* address. */
if (host_start && !host_size) {
- if (validate_guest_space(host_start, host_size) == 1) {
- return host_start;
- } else {
+#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
+ if (init_guest_commpage(host_start, host_size) != 1) {
return (unsigned long)-1;
}
+#endif
+ return host_start;
}
/* Setup the initial flags and start address. */
@@ -1908,7 +1890,8 @@ unsigned long init_guest_space(unsigned long host_start,
/* Otherwise, a non-zero size region of memory needs to be mapped
* and validated. */
while (1) {
- unsigned long real_size = host_size;
+ unsigned long real_start, real_size, aligned_size;
+ aligned_size = real_size = host_size;
/* Do not use mmap_find_vma here because that is limited to the
* guest address space. We are going to make the
@@ -1920,30 +1903,63 @@ unsigned long init_guest_space(unsigned long host_start,
return (unsigned long)-1;
}
+ /* Check to see if the address is valid. */
+ if (host_start && real_start != current_start) {
+ goto try_again;
+ }
+
/* Ensure the address is properly aligned. */
if (real_start & ~qemu_host_page_mask) {
+ /* Ideally, we adjust like
+ *
+ * pages: [ ][ ][ ][ ][ ]
+ * old: [ real ]
+ * [ aligned ]
+ * new: [ real ]
+ * [ aligned ]
+ *
+ * But if there is something else mapped right after it,
+ * then obviously it won't have room to grow, and the
+ * kernel will put the new larger real someplace else with
+ * unknown alignment (if we made it to here, then
+ * fixed=false). Which is why we grow real by a full page
+ * size, instead of by part of one; so that even if we get
+ * moved, we can still guarantee alignment. But this does
+ * mean that there is a padding of < 1 page both before
+ * and after the aligned range; the "after" could could
+ * cause problems for ARM emulation where it could butt in
+ * to where we need to put the commpage.
+ */
munmap((void *)real_start, host_size);
- real_size = host_size + qemu_host_page_size;
+ real_size = aligned_size + qemu_host_page_size;
real_start = (unsigned long)
mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0);
if (real_start == (unsigned long)-1) {
return (unsigned long)-1;
}
- real_start = HOST_PAGE_ALIGN(real_start);
+ aligned_start = HOST_PAGE_ALIGN(real_start);
+ } else {
+ aligned_start = real_start;
}
- /* Check to see if the address is valid. */
- if (!host_start || real_start == current_start) {
- int valid = validate_guest_space(real_start - guest_start,
- real_size);
- if (valid == 1) {
- break;
- } else if (valid == -1) {
- return (unsigned long)-1;
- }
- /* valid == 0, so try again. */
+#if defined(TARGET_ARM) && !defined(TARGET_AARCH64)
+ /* On 32-bit ARM, we need to also be able to map the commpage. */
+ int valid = init_guest_commpage(aligned_start - guest_start,
+ aligned_size + guest_start);
+ if (valid == -1) {
+ munmap((void *)real_start, real_size);
+ return (unsigned long)-1;
+ } else if (valid == 0) {
+ goto try_again;
}
+#endif
+
+ /* If nothing has said `return -1` or `goto try_again` yet,
+ * then the address we have is good.
+ */
+ break;
+ try_again:
/* That address didn't work. Unmap and try a different one.
* The address the host picked because is typically right at
* the top of the host address space and leaves the guest with
@@ -1952,8 +1968,12 @@ unsigned long init_guest_space(unsigned long host_start,
* happen often. Probably means we got unlucky and host
* address space randomization put a shared library somewhere
* inconvenient.
+ *
+ * This is probably a good strategy if host_start, but is
+ * probably a bad strategy if not, which means we got here
+ * because of trouble with ARM commpage setup.
*/
- munmap((void *)real_start, host_size);
+ munmap((void *)real_start, real_size);
current_start += qemu_host_page_size;
if (host_start == current_start) {
/* Theoretically possible if host doesn't have any suitably
@@ -1965,7 +1985,7 @@ unsigned long init_guest_space(unsigned long host_start,
qemu_log_mask(CPU_LOG_PAGE, "Reserved 0x%lx bytes of guest address space\n", host_size);
- return real_start;
+ return aligned_start;
}
static void probe_guest_base(const char *image_name,
diff --git a/linux-user/main.c b/linux-user/main.c
index 7bc9bc79b0..ba09b7d0c8 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -45,6 +45,7 @@ static const char *argv0;
static int gdbstub_port;
static envlist_t *envlist;
static const char *cpu_model;
+static const char *cpu_type;
unsigned long mmap_min_addr;
unsigned long guest_base;
int have_guest_base;
@@ -884,95 +885,6 @@ void cpu_loop(CPUARMState *env)
#endif
-#ifdef TARGET_UNICORE32
-
-void cpu_loop(CPUUniCore32State *env)
-{
- CPUState *cs = CPU(uc32_env_get_cpu(env));
- int trapnr;
- unsigned int n, insn;
- target_siginfo_t info;
-
- for (;;) {
- cpu_exec_start(cs);
- trapnr = cpu_exec(cs);
- cpu_exec_end(cs);
- process_queued_cpu_work(cs);
-
- switch (trapnr) {
- case UC32_EXCP_PRIV:
- {
- /* system call */
- get_user_u32(insn, env->regs[31] - 4);
- n = insn & 0xffffff;
-
- if (n >= UC32_SYSCALL_BASE) {
- /* linux syscall */
- n -= UC32_SYSCALL_BASE;
- if (n == UC32_SYSCALL_NR_set_tls) {
- cpu_set_tls(env, env->regs[0]);
- env->regs[0] = 0;
- } else {
- abi_long ret = do_syscall(env,
- n,
- env->regs[0],
- env->regs[1],
- env->regs[2],
- env->regs[3],
- env->regs[4],
- env->regs[5],
- 0, 0);
- if (ret == -TARGET_ERESTARTSYS) {
- env->regs[31] -= 4;
- } else if (ret != -TARGET_QEMU_ESIGRETURN) {
- env->regs[0] = ret;
- }
- }
- } else {
- goto error;
- }
- }
- break;
- case UC32_EXCP_DTRAP:
- case UC32_EXCP_ITRAP:
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->cp0.c4_faultaddr;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
- case EXCP_INTERRUPT:
- /* just indicate that signals should be handled asap */
- break;
- case EXCP_DEBUG:
- {
- int sig;
-
- sig = gdb_handlesig(cs, TARGET_SIGTRAP);
- if (sig) {
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- }
- break;
- case EXCP_ATOMIC:
- cpu_exec_step_atomic(cs);
- break;
- default:
- goto error;
- }
- process_pending_signals(env);
- }
-
-error:
- EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
- abort();
-}
-#endif
-
#ifdef TARGET_SPARC
#define SPARC64_STACK_BIAS 2047
@@ -3930,6 +3842,242 @@ void cpu_loop(CPUHPPAState *env)
#endif /* TARGET_HPPA */
+#ifdef TARGET_XTENSA
+
+static void xtensa_rfw(CPUXtensaState *env)
+{
+ xtensa_restore_owb(env);
+ env->pc = env->sregs[EPC1];
+}
+
+static void xtensa_rfwu(CPUXtensaState *env)
+{
+ env->sregs[WINDOW_START] |= (1 << env->sregs[WINDOW_BASE]);
+ xtensa_rfw(env);
+}
+
+static void xtensa_rfwo(CPUXtensaState *env)
+{
+ env->sregs[WINDOW_START] &= ~(1 << env->sregs[WINDOW_BASE]);
+ xtensa_rfw(env);
+}
+
+static void xtensa_overflow4(CPUXtensaState *env)
+{
+ put_user_ual(env->regs[0], env->regs[5] - 16);
+ put_user_ual(env->regs[1], env->regs[5] - 12);
+ put_user_ual(env->regs[2], env->regs[5] - 8);
+ put_user_ual(env->regs[3], env->regs[5] - 4);
+ xtensa_rfwo(env);
+}
+
+static void xtensa_underflow4(CPUXtensaState *env)
+{
+ get_user_ual(env->regs[0], env->regs[5] - 16);
+ get_user_ual(env->regs[1], env->regs[5] - 12);
+ get_user_ual(env->regs[2], env->regs[5] - 8);
+ get_user_ual(env->regs[3], env->regs[5] - 4);
+ xtensa_rfwu(env);
+}
+
+static void xtensa_overflow8(CPUXtensaState *env)
+{
+ put_user_ual(env->regs[0], env->regs[9] - 16);
+ get_user_ual(env->regs[0], env->regs[1] - 12);
+ put_user_ual(env->regs[1], env->regs[9] - 12);
+ put_user_ual(env->regs[2], env->regs[9] - 8);
+ put_user_ual(env->regs[3], env->regs[9] - 4);
+ put_user_ual(env->regs[4], env->regs[0] - 32);
+ put_user_ual(env->regs[5], env->regs[0] - 28);
+ put_user_ual(env->regs[6], env->regs[0] - 24);
+ put_user_ual(env->regs[7], env->regs[0] - 20);
+ xtensa_rfwo(env);
+}
+
+static void xtensa_underflow8(CPUXtensaState *env)
+{
+ get_user_ual(env->regs[0], env->regs[9] - 16);
+ get_user_ual(env->regs[1], env->regs[9] - 12);
+ get_user_ual(env->regs[2], env->regs[9] - 8);
+ get_user_ual(env->regs[7], env->regs[1] - 12);
+ get_user_ual(env->regs[3], env->regs[9] - 4);
+ get_user_ual(env->regs[4], env->regs[7] - 32);
+ get_user_ual(env->regs[5], env->regs[7] - 28);
+ get_user_ual(env->regs[6], env->regs[7] - 24);
+ get_user_ual(env->regs[7], env->regs[7] - 20);
+ xtensa_rfwu(env);
+}
+
+static void xtensa_overflow12(CPUXtensaState *env)
+{
+ put_user_ual(env->regs[0], env->regs[13] - 16);
+ get_user_ual(env->regs[0], env->regs[1] - 12);
+ put_user_ual(env->regs[1], env->regs[13] - 12);
+ put_user_ual(env->regs[2], env->regs[13] - 8);
+ put_user_ual(env->regs[3], env->regs[13] - 4);
+ put_user_ual(env->regs[4], env->regs[0] - 48);
+ put_user_ual(env->regs[5], env->regs[0] - 44);
+ put_user_ual(env->regs[6], env->regs[0] - 40);
+ put_user_ual(env->regs[7], env->regs[0] - 36);
+ put_user_ual(env->regs[8], env->regs[0] - 32);
+ put_user_ual(env->regs[9], env->regs[0] - 28);
+ put_user_ual(env->regs[10], env->regs[0] - 24);
+ put_user_ual(env->regs[11], env->regs[0] - 20);
+ xtensa_rfwo(env);
+}
+
+static void xtensa_underflow12(CPUXtensaState *env)
+{
+ get_user_ual(env->regs[0], env->regs[13] - 16);
+ get_user_ual(env->regs[1], env->regs[13] - 12);
+ get_user_ual(env->regs[2], env->regs[13] - 8);
+ get_user_ual(env->regs[11], env->regs[1] - 12);
+ get_user_ual(env->regs[3], env->regs[13] - 4);
+ get_user_ual(env->regs[4], env->regs[11] - 48);
+ get_user_ual(env->regs[5], env->regs[11] - 44);
+ get_user_ual(env->regs[6], env->regs[11] - 40);
+ get_user_ual(env->regs[7], env->regs[11] - 36);
+ get_user_ual(env->regs[8], env->regs[11] - 32);
+ get_user_ual(env->regs[9], env->regs[11] - 28);
+ get_user_ual(env->regs[10], env->regs[11] - 24);
+ get_user_ual(env->regs[11], env->regs[11] - 20);
+ xtensa_rfwu(env);
+}
+
+void cpu_loop(CPUXtensaState *env)
+{
+ CPUState *cs = CPU(xtensa_env_get_cpu(env));
+ target_siginfo_t info;
+ abi_ulong ret;
+ int trapnr;
+
+ while (1) {
+ cpu_exec_start(cs);
+ trapnr = cpu_exec(cs);
+ cpu_exec_end(cs);
+ process_queued_cpu_work(cs);
+
+ env->sregs[PS] &= ~PS_EXCM;
+ switch (trapnr) {
+ case EXCP_INTERRUPT:
+ break;
+
+ case EXC_WINDOW_OVERFLOW4:
+ xtensa_overflow4(env);
+ break;
+ case EXC_WINDOW_UNDERFLOW4:
+ xtensa_underflow4(env);
+ break;
+ case EXC_WINDOW_OVERFLOW8:
+ xtensa_overflow8(env);
+ break;
+ case EXC_WINDOW_UNDERFLOW8:
+ xtensa_underflow8(env);
+ break;
+ case EXC_WINDOW_OVERFLOW12:
+ xtensa_overflow12(env);
+ break;
+ case EXC_WINDOW_UNDERFLOW12:
+ xtensa_underflow12(env);
+ break;
+
+ case EXC_USER:
+ switch (env->sregs[EXCCAUSE]) {
+ case ILLEGAL_INSTRUCTION_CAUSE:
+ case PRIVILEGED_CAUSE:
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code =
+ env->sregs[EXCCAUSE] == ILLEGAL_INSTRUCTION_CAUSE ?
+ TARGET_ILL_ILLOPC : TARGET_ILL_PRVOPC;
+ info._sifields._sigfault._addr = env->sregs[EPC1];
+ queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
+ break;
+
+ case SYSCALL_CAUSE:
+ env->pc += 3;
+ ret = do_syscall(env, env->regs[2],
+ env->regs[6], env->regs[3],
+ env->regs[4], env->regs[5],
+ env->regs[8], env->regs[9], 0, 0);
+ switch (ret) {
+ default:
+ env->regs[2] = ret;
+ break;
+
+ case -TARGET_ERESTARTSYS:
+ case -TARGET_QEMU_ESIGRETURN:
+ break;
+ }
+ break;
+
+ case ALLOCA_CAUSE:
+ env->sregs[PS] = deposit32(env->sregs[PS],
+ PS_OWB_SHIFT,
+ PS_OWB_LEN,
+ env->sregs[WINDOW_BASE]);
+
+ switch (env->regs[0] & 0xc0000000) {
+ case 0x00000000:
+ case 0x40000000:
+ xtensa_rotate_window(env, -1);
+ xtensa_underflow4(env);
+ break;
+
+ case 0x80000000:
+ xtensa_rotate_window(env, -2);
+ xtensa_underflow8(env);
+ break;
+
+ case 0xc0000000:
+ xtensa_rotate_window(env, -3);
+ xtensa_underflow12(env);
+ break;
+ }
+ break;
+
+ case INTEGER_DIVIDE_BY_ZERO_CAUSE:
+ info.si_signo = TARGET_SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_INTDIV;
+ info._sifields._sigfault._addr = env->sregs[EPC1];
+ queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
+ break;
+
+ case LOAD_PROHIBITED_CAUSE:
+ case STORE_PROHIBITED_CAUSE:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_ACCERR;
+ info._sifields._sigfault._addr = env->sregs[EXCVADDR];
+ queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
+ break;
+
+ default:
+ fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]);
+ g_assert_not_reached();
+ }
+ break;
+ case EXCP_DEBUG:
+ trapnr = gdb_handlesig(cs, TARGET_SIGTRAP);
+ if (trapnr) {
+ info.si_signo = trapnr;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, trapnr, QEMU_SI_FAULT, &info);
+ }
+ break;
+ case EXC_DEBUG:
+ default:
+ fprintf(stderr, "trapnr = %d\n", trapnr);
+ g_assert_not_reached();
+ }
+ process_pending_signals(env);
+ }
+}
+
+#endif /* TARGET_XTENSA */
+
__thread CPUState *thread_cpu;
bool qemu_cpu_is_self(CPUState *cpu)
@@ -3967,7 +4115,7 @@ void init_task_state(TaskState *ts)
CPUArchState *cpu_copy(CPUArchState *env)
{
CPUState *cpu = ENV_GET_CPU(env);
- CPUState *new_cpu = cpu_init(cpu_model);
+ CPUState *new_cpu = cpu_create(cpu_type);
CPUArchState *new_env = new_cpu->env_ptr;
CPUBreakpoint *bp;
CPUWatchpoint *wp;
@@ -4163,7 +4311,7 @@ static void handle_arg_strace(const char *arg)
static void handle_arg_version(const char *arg)
{
- printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION
+ printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
"\n" QEMU_COPYRIGHT "\n");
exit(EXIT_SUCCESS);
}
@@ -4450,10 +4598,13 @@ int main(int argc, char **argv, char **envp)
if (cpu_model == NULL) {
cpu_model = cpu_get_model(get_elf_eflags(execfd));
}
+ cpu_type = parse_cpu_model(cpu_model);
+
tcg_exec_init(0);
/* NOTE: we need to init the CPU at this stage to get
qemu_host_page_size */
- cpu = cpu_init(cpu_model);
+
+ cpu = cpu_create(cpu_type);
env = cpu->env_ptr;
cpu_reset(cpu);
@@ -4737,14 +4888,6 @@ int main(int argc, char **argv, char **envp)
}
#endif
}
-#elif defined(TARGET_UNICORE32)
- {
- int i;
- cpu_asr_write(env, regs->uregs[32], 0xffffffff);
- for (i = 0; i < 32; i++) {
- env->regs[i] = regs->uregs[i];
- }
- }
#elif defined(TARGET_SPARC)
{
int i;
@@ -4970,11 +5113,20 @@ int main(int argc, char **argv, char **envp)
env->iaoq_f = regs->iaoq[0];
env->iaoq_b = regs->iaoq[1];
}
+#elif defined(TARGET_XTENSA)
+ {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ env->regs[i] = regs->areg[i];
+ }
+ env->sregs[WINDOW_START] = regs->windowstart;
+ env->pc = regs->pc;
+ }
#else
#error unsupported target CPU
#endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
+#if defined(TARGET_ARM) || defined(TARGET_M68K)
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 0fbfd6dff2..9168a2051c 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -77,11 +77,12 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot)
#endif
if ((start & ~TARGET_PAGE_MASK) != 0)
- return -EINVAL;
+ return -TARGET_EINVAL;
len = TARGET_PAGE_ALIGN(len);
end = start + len;
- if (end < start)
- return -EINVAL;
+ if (!guest_range_valid(start, len)) {
+ return -TARGET_ENOMEM;
+ }
prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
if (len == 0)
return 0;
@@ -481,8 +482,8 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
* It can fail only on 64-bit host with 32-bit target.
* On any other target/host host mmap() handles this error correctly.
*/
- if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
- errno = EINVAL;
+ if (!guest_range_valid(start, len)) {
+ errno = ENOMEM;
goto fail;
}
@@ -620,10 +621,12 @@ int target_munmap(abi_ulong start, abi_ulong len)
start, len);
#endif
if (start & ~TARGET_PAGE_MASK)
- return -EINVAL;
+ return -TARGET_EINVAL;
len = TARGET_PAGE_ALIGN(len);
- if (len == 0)
- return -EINVAL;
+ if (len == 0 || !guest_range_valid(start, len)) {
+ return -TARGET_EINVAL;
+ }
+
mmap_lock();
end = start + len;
real_start = start & qemu_host_page_mask;
@@ -678,6 +681,13 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
int prot;
void *host_addr;
+ if (!guest_range_valid(old_addr, old_size) ||
+ ((flags & MREMAP_FIXED) &&
+ !guest_range_valid(new_addr, new_size))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
mmap_lock();
if (flags & MREMAP_FIXED) {
@@ -744,20 +754,3 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
mmap_unlock();
return new_addr;
}
-
-int target_msync(abi_ulong start, abi_ulong len, int flags)
-{
- abi_ulong end;
-
- if (start & ~TARGET_PAGE_MASK)
- return -EINVAL;
- len = TARGET_PAGE_ALIGN(len);
- end = start + len;
- if (end < start)
- return -EINVAL;
- if (end == start)
- return 0;
-
- start &= qemu_host_page_mask;
- return msync(g2h(start), end - start, flags);
-}
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index f4b4ca72ad..192a0d2fef 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -100,9 +100,6 @@ typedef struct TaskState {
# endif
int swi_errno;
#endif
-#ifdef TARGET_UNICORE32
- int swi_errno;
-#endif
#if defined(TARGET_I386) && !defined(TARGET_X86_64)
abi_ulong target_v86;
struct vm86_saved_state vm86_saved_regs;
@@ -115,7 +112,7 @@ typedef struct TaskState {
int sim_syscalls;
abi_ulong tp_value;
#endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
+#if defined(TARGET_ARM) || defined(TARGET_M68K)
/* Extra fields for semihosted binaries. */
abi_ulong heap_base;
abi_ulong heap_limit;
@@ -428,7 +425,6 @@ int target_munmap(abi_ulong start, abi_ulong len);
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
abi_ulong new_size, unsigned long flags,
abi_ulong new_addr);
-int target_msync(abi_ulong start, abi_ulong len, int flags);
extern unsigned long last_brk;
extern abi_ulong mmap_next_start;
abi_ulong mmap_find_vma(abi_ulong, abi_ulong);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 2ce5d7a3c7..2ea3e0321f 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -253,8 +253,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
return 0;
}
-#if !defined(TARGET_OPENRISC) && !defined(TARGET_UNICORE32) && \
- !defined(TARGET_NIOS2)
+#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2)
/* Just set the guest's signal mask to the specified value; the
* caller is assumed to have called block_signals() already.
*/
@@ -512,7 +511,6 @@ void signal_init(void)
}
}
-#ifndef TARGET_UNICORE32
/* Force a synchronously taken signal. The kernel force_sig() function
* also forces the signal to "not blocked, not ignored", but for QEMU
* that work is done in process_pending_signals().
@@ -546,7 +544,6 @@ static void force_sigsegv(int oldsig)
}
force_sig(TARGET_SIGSEGV);
}
-#endif
#endif
@@ -7051,33 +7048,262 @@ long do_rt_sigreturn(CPUArchState *env)
return -TARGET_QEMU_ESIGRETURN;
}
-#else
+#elif defined(TARGET_XTENSA)
-static void setup_frame(int sig, struct target_sigaction *ka,
- target_sigset_t *set, CPUArchState *env)
+struct target_sigcontext {
+ abi_ulong sc_pc;
+ abi_ulong sc_ps;
+ abi_ulong sc_lbeg;
+ abi_ulong sc_lend;
+ abi_ulong sc_lcount;
+ abi_ulong sc_sar;
+ abi_ulong sc_acclo;
+ abi_ulong sc_acchi;
+ abi_ulong sc_a[16];
+ abi_ulong sc_xtregs;
+};
+
+struct target_ucontext {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask;
+};
+
+struct target_rt_sigframe {
+ target_siginfo_t info;
+ struct target_ucontext uc;
+ /* TODO: xtregs */
+ uint8_t retcode[6];
+ abi_ulong window[4];
+};
+
+static abi_ulong get_sigframe(struct target_sigaction *sa,
+ CPUXtensaState *env,
+ unsigned long framesize)
{
- fprintf(stderr, "setup_frame: not implemented\n");
+ abi_ulong sp = env->regs[1];
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) {
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+ return (sp - framesize) & -16;
+}
+
+static int flush_window_regs(CPUXtensaState *env)
+{
+ const uint32_t nareg_mask = env->config->nareg - 1;
+ uint32_t wb = env->sregs[WINDOW_BASE];
+ uint32_t ws = (xtensa_replicate_windowstart(env) >> (wb + 1)) &
+ ((1 << env->config->nareg / 4) - 1);
+ uint32_t d = ctz32(ws) + 1;
+ uint32_t sp;
+ abi_long ret = 0;
+
+ wb += d;
+ ws >>= d;
+
+ xtensa_sync_phys_from_window(env);
+ sp = env->phys_regs[(wb * 4 + 1) & nareg_mask];
+
+ while (ws && ret == 0) {
+ int d;
+ int i;
+ int idx;
+
+ if (ws & 0x1) {
+ ws >>= 1;
+ d = 1;
+ } else if (ws & 0x2) {
+ ws >>= 2;
+ d = 2;
+ for (i = 0; i < 4; ++i) {
+ idx = (wb * 4 + 4 + i) & nareg_mask;
+ ret |= put_user_ual(env->phys_regs[idx], sp + (i - 12) * 4);
+ }
+ } else if (ws & 0x4) {
+ ws >>= 3;
+ d = 3;
+ for (i = 0; i < 8; ++i) {
+ idx = (wb * 4 + 4 + i) & nareg_mask;
+ ret |= put_user_ual(env->phys_regs[idx], sp + (i - 16) * 4);
+ }
+ } else {
+ g_assert_not_reached();
+ }
+ sp = env->phys_regs[((wb + d) * 4 + 1) & nareg_mask];
+ for (i = 0; i < 4; ++i) {
+ idx = (wb * 4 + i) & nareg_mask;
+ ret |= put_user_ual(env->phys_regs[idx], sp + (i - 4) * 4);
+ }
+ wb += d;
+ }
+ return ret == 0;
+}
+
+static int setup_sigcontext(struct target_rt_sigframe *frame,
+ CPUXtensaState *env)
+{
+ struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
+ int i;
+
+ __put_user(env->pc, &sc->sc_pc);
+ __put_user(env->sregs[PS], &sc->sc_ps);
+ __put_user(env->sregs[LBEG], &sc->sc_lbeg);
+ __put_user(env->sregs[LEND], &sc->sc_lend);
+ __put_user(env->sregs[LCOUNT], &sc->sc_lcount);
+ if (!flush_window_regs(env)) {
+ return 0;
+ }
+ for (i = 0; i < 16; ++i) {
+ __put_user(env->regs[i], sc->sc_a + i);
+ }
+ __put_user(0, &sc->sc_xtregs);
+ /* TODO: xtregs */
+ return 1;
}
static void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
- target_sigset_t *set, CPUArchState *env)
+ target_sigset_t *set, CPUXtensaState *env)
{
- fprintf(stderr, "setup_rt_frame: not implemented\n");
+ abi_ulong frame_addr;
+ struct target_rt_sigframe *frame;
+ uint32_t ra;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ trace_user_setup_rt_frame(env, frame_addr);
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ goto give_sigsegv;
+ }
+
+ if (ka->sa_flags & SA_SIGINFO) {
+ tswap_siginfo(&frame->info, info);
+ }
+
+ __put_user(0, &frame->uc.tuc_flags);
+ __put_user(0, &frame->uc.tuc_link);
+ __put_user(target_sigaltstack_used.ss_sp,
+ &frame->uc.tuc_stack.ss_sp);
+ __put_user(sas_ss_flags(env->regs[1]),
+ &frame->uc.tuc_stack.ss_flags);
+ __put_user(target_sigaltstack_used.ss_size,
+ &frame->uc.tuc_stack.ss_size);
+ if (!setup_sigcontext(frame, env)) {
+ unlock_user_struct(frame, frame_addr, 0);
+ goto give_sigsegv;
+ }
+ for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
+ __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
+ }
+
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ ra = ka->sa_restorer;
+ } else {
+ ra = frame_addr + offsetof(struct target_rt_sigframe, retcode);
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ __put_user(0x22, &frame->retcode[0]);
+ __put_user(0x0a, &frame->retcode[1]);
+ __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
+ /* Generate instruction: SYSCALL */
+ __put_user(0x00, &frame->retcode[3]);
+ __put_user(0x05, &frame->retcode[4]);
+ __put_user(0x00, &frame->retcode[5]);
+#else
+ /* Generate instruction: MOVI a2, __NR_rt_sigreturn */
+ __put_user(0x22, &frame->retcode[0]);
+ __put_user(0xa0, &frame->retcode[1]);
+ __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]);
+ /* Generate instruction: SYSCALL */
+ __put_user(0x00, &frame->retcode[3]);
+ __put_user(0x50, &frame->retcode[4]);
+ __put_user(0x00, &frame->retcode[5]);
+#endif
+ }
+ env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);
+ if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) {
+ env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT);
+ }
+ memset(env->regs, 0, sizeof(env->regs));
+ env->pc = ka->_sa_handler;
+ env->regs[1] = frame_addr;
+ env->sregs[WINDOW_BASE] = 0;
+ env->sregs[WINDOW_START] = 1;
+
+ env->regs[4] = (ra & 0x3fffffff) | 0x40000000;
+ env->regs[6] = sig;
+ env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info);
+ env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc);
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ force_sigsegv(sig);
+ return;
}
-long do_sigreturn(CPUArchState *env)
+static void restore_sigcontext(CPUXtensaState *env,
+ struct target_rt_sigframe *frame)
{
- fprintf(stderr, "do_sigreturn: not implemented\n");
- return -TARGET_ENOSYS;
+ struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
+ uint32_t ps;
+ int i;
+
+ __get_user(env->pc, &sc->sc_pc);
+ __get_user(ps, &sc->sc_ps);
+ __get_user(env->sregs[LBEG], &sc->sc_lbeg);
+ __get_user(env->sregs[LEND], &sc->sc_lend);
+ __get_user(env->sregs[LCOUNT], &sc->sc_lcount);
+
+ env->sregs[WINDOW_BASE] = 0;
+ env->sregs[WINDOW_START] = 1;
+ env->sregs[PS] = deposit32(env->sregs[PS],
+ PS_CALLINC_SHIFT,
+ PS_CALLINC_LEN,
+ extract32(ps, PS_CALLINC_SHIFT,
+ PS_CALLINC_LEN));
+ for (i = 0; i < 16; ++i) {
+ __get_user(env->regs[i], sc->sc_a + i);
+ }
+ /* TODO: xtregs */
}
-long do_rt_sigreturn(CPUArchState *env)
+long do_rt_sigreturn(CPUXtensaState *env)
{
- fprintf(stderr, "do_rt_sigreturn: not implemented\n");
- return -TARGET_ENOSYS;
+ abi_ulong frame_addr = env->regs[1];
+ struct target_rt_sigframe *frame;
+ sigset_t set;
+
+ trace_user_do_rt_sigreturn(env, frame_addr);
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ goto badframe;
+ }
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
+ set_sigmask(&set);
+
+ restore_sigcontext(env, frame);
+
+ if (do_sigaltstack(frame_addr +
+ offsetof(struct target_rt_sigframe, uc.tuc_stack),
+ 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) {
+ goto badframe;
+ }
+ unlock_user_struct(frame, frame_addr, 0);
+ return -TARGET_QEMU_ESIGRETURN;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return -TARGET_QEMU_ESIGRETURN;
}
+#else
+#error Target needs to add support for signal handling
#endif
static void handle_pending_signal(CPUArchState *cpu_env, int sig,
@@ -7154,7 +7380,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
|| defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \
|| defined(TARGET_PPC64) || defined(TARGET_HPPA) \
|| defined(TARGET_NIOS2) || defined(TARGET_X86_64) \
- || defined(TARGET_RISCV)
+ || defined(TARGET_RISCV) || defined(TARGET_XTENSA)
/* These targets do not have traditional signals. */
setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env);
#else
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index b4f7b14fbe..889abbda1e 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -709,6 +709,8 @@ static inline int regpairs_aligned(void *cpu_env, int num)
return 0;
}
}
+#elif defined(TARGET_XTENSA)
+static inline int regpairs_aligned(void *cpu_env, int num) { return 1; }
#else
static inline int regpairs_aligned(void *cpu_env, int num) { return 0; }
#endif
@@ -4900,6 +4902,9 @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env,
return -TARGET_EINVAL;
}
}
+ if (!guest_range_valid(shmaddr, shm_info.shm_segsz)) {
+ return -TARGET_EINVAL;
+ }
mmap_lock();
@@ -4944,6 +4949,9 @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env,
static inline abi_long do_shmdt(abi_ulong shmaddr)
{
int i;
+ abi_long rv;
+
+ mmap_lock();
for (i = 0; i < N_SHM_REGIONS; ++i) {
if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) {
@@ -4952,8 +4960,11 @@ static inline abi_long do_shmdt(abi_ulong shmaddr)
break;
}
}
+ rv = get_errno(shmdt(g2h(shmaddr)));
- return get_errno(shmdt(g2h(shmaddr)));
+ mmap_unlock();
+
+ return rv;
}
#ifdef TARGET_NR_ipc
@@ -7468,7 +7479,7 @@ static int open_self_maps(void *cpu_env, int fd)
}
if (h2g_valid(min)) {
int flags = page_get_flags(h2g(min));
- max = h2g_valid(max - 1) ? max : (uintptr_t)g2h(GUEST_ADDR_MAX);
+ max = h2g_valid(max - 1) ? max : (uintptr_t)g2h(GUEST_ADDR_MAX) + 1;
if (page_check_range(h2g(min), max - min, flags) == -1) {
continue;
}
@@ -9545,6 +9556,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
__put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]);
__put_user(stfs.f_namelen, &target_stfs->f_namelen);
__put_user(stfs.f_frsize, &target_stfs->f_frsize);
+#ifdef _STATFS_F_FLAGS
+ __put_user(stfs.f_flags, &target_stfs->f_flags);
+#else
+ __put_user(0, &target_stfs->f_flags);
+#endif
memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare));
unlock_user_struct(target_stfs, arg2, 1);
}
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index e00e1b3862..13fe840239 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -69,9 +69,10 @@
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \
|| defined(TARGET_M68K) || defined(TARGET_CRIS) \
- || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \
+ || defined(TARGET_S390X) \
|| defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \
- || defined(TARGET_NIOS2) || defined(TARGET_RISCV)
+ || defined(TARGET_NIOS2) || defined(TARGET_RISCV) \
+ || defined(TARGET_XTENSA)
#define TARGET_IOC_SIZEBITS 14
#define TARGET_IOC_DIRBITS 2
@@ -352,19 +353,6 @@ typedef struct {
int val[2];
} kernel_fsid_t;
-struct kernel_statfs {
- int f_type;
- int f_bsize;
- int f_blocks;
- int f_bfree;
- int f_bavail;
- int f_files;
- int f_ffree;
- kernel_fsid_t f_fsid;
- int f_namelen;
- int f_spare[6];
-};
-
struct target_dirent {
abi_long d_ino;
abi_long d_off;
@@ -433,10 +421,10 @@ int do_sigaction(int sig, const struct target_sigaction *act,
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \
|| defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \
|| defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \
- || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \
+ || defined(TARGET_MICROBLAZE) \
|| defined(TARGET_S390X) || defined(TARGET_OPENRISC) \
|| defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \
- || defined(TARGET_RISCV)
+ || defined(TARGET_RISCV) || defined(TARGET_XTENSA)
#if defined(TARGET_SPARC)
#define TARGET_SA_NOCLDSTOP 8u
@@ -1392,6 +1380,18 @@ struct target_winsize {
#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */
#define TARGET_MAP_STACK 0x40000 /* ignored */
#define TARGET_MAP_HUGETLB 0x80000 /* create a huge page mapping */
+#elif defined(TARGET_XTENSA)
+#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
+#define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x4000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x8000 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */
+#define TARGET_MAP_POPULATE 0x10000 /* populate (prefault) pagetables */
+#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */
+#define TARGET_MAP_STACK 0x40000
+#define TARGET_MAP_HUGETLB 0x80000 /* create a huge page mapping */
#else
#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */
@@ -1409,7 +1409,7 @@ struct target_winsize {
#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \
|| (defined(TARGET_ARM) && defined(TARGET_ABI32)) \
- || defined(TARGET_CRIS) || defined(TARGET_UNICORE32)
+ || defined(TARGET_CRIS)
struct target_stat {
unsigned short st_dev;
unsigned short __pad1;
@@ -2093,6 +2093,51 @@ struct target_stat {
abi_ulong target_st_ctime_nsec;
unsigned int __unused[2];
};
+#elif defined(TARGET_XTENSA)
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ abi_ulong st_rdev;
+ abi_long st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ abi_ulong target_st_atime;
+ abi_ulong target_st_atime_nsec;
+ abi_ulong target_st_mtime;
+ abi_ulong target_st_mtime_nsec;
+ abi_ulong target_st_ctime;
+ abi_ulong target_st_ctime_nsec;
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+#define TARGET_HAS_STRUCT_STAT64
+struct target_stat64 {
+ uint64_t st_dev; /* Device */
+ uint64_t st_ino; /* File serial number */
+ unsigned int st_mode; /* File mode. */
+ unsigned int st_nlink; /* Link count. */
+ unsigned int st_uid; /* User ID of the file's owner. */
+ unsigned int st_gid; /* Group ID of the file's group. */
+ uint64_t st_rdev; /* Device number, if device. */
+ int64_t st_size; /* Size of file, in bytes. */
+ abi_ulong st_blksize; /* Optimal block size for I/O. */
+ abi_ulong __unused2;
+ uint64_t st_blocks; /* Number 512-byte blocks allocated. */
+ abi_ulong target_st_atime; /* Time of last access. */
+ abi_ulong target_st_atime_nsec;
+ abi_ulong target_st_mtime; /* Time of last modification. */
+ abi_ulong target_st_mtime_nsec;
+ abi_ulong target_st_ctime; /* Time of last status change. */
+ abi_ulong target_st_ctime_nsec;
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
#elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) || \
defined(TARGET_NIOS2) || defined(TARGET_RISCV)
@@ -2226,7 +2271,8 @@ struct target_statfs {
/* Linux specials */
target_fsid_t f_fsid;
int32_t f_namelen;
- int32_t f_spare[6];
+ int32_t f_flags;
+ int32_t f_spare[5];
};
#else
struct target_statfs {
@@ -2242,7 +2288,8 @@ struct target_statfs {
/* Linux specials */
target_fsid_t f_fsid;
abi_long f_namelen;
- abi_long f_spare[6];
+ abi_long f_flags;
+ abi_long f_spare[5];
};
#endif
@@ -2258,7 +2305,8 @@ struct target_statfs64 {
uint64_t f_bavail;
target_fsid_t f_fsid;
uint32_t f_namelen;
- uint32_t f_spare[6];
+ uint32_t f_flags;
+ uint32_t f_spare[5];
};
#elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \
defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \
@@ -2274,7 +2322,8 @@ struct target_statfs {
target_fsid_t f_fsid;
abi_long f_namelen;
abi_long f_frsize;
- abi_long f_spare[5];
+ abi_long f_flags;
+ abi_long f_spare[4];
};
struct target_statfs64 {
@@ -2288,7 +2337,8 @@ struct target_statfs64 {
target_fsid_t f_fsid;
abi_long f_namelen;
abi_long f_frsize;
- abi_long f_spare[5];
+ abi_long f_flags;
+ abi_long f_spare[4];
};
#elif defined(TARGET_S390X)
struct target_statfs {
@@ -2302,7 +2352,9 @@ struct target_statfs {
kernel_fsid_t f_fsid;
int32_t f_namelen;
int32_t f_frsize;
- int32_t f_spare[5];
+ int32_t f_flags;
+ int32_t f_spare[4];
+
};
struct target_statfs64 {
@@ -2316,7 +2368,8 @@ struct target_statfs64 {
kernel_fsid_t f_fsid;
int32_t f_namelen;
int32_t f_frsize;
- int32_t f_spare[5];
+ int32_t f_flags;
+ int32_t f_spare[4];
};
#else
struct target_statfs {
@@ -2330,7 +2383,8 @@ struct target_statfs {
target_fsid_t f_fsid;
uint32_t f_namelen;
uint32_t f_frsize;
- uint32_t f_spare[5];
+ uint32_t f_flags;
+ uint32_t f_spare[4];
};
struct target_statfs64 {
@@ -2344,7 +2398,8 @@ struct target_statfs64 {
target_fsid_t f_fsid;
uint32_t f_namelen;
uint32_t f_frsize;
- uint32_t f_spare[5];
+ uint32_t f_flags;
+ uint32_t f_spare[4];
};
#endif
@@ -2593,7 +2648,8 @@ struct target_flock64 {
short l_whence;
#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \
|| defined(TARGET_SPARC) || defined(TARGET_HPPA) \
- || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX)
+ || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) \
+ || defined(TARGET_XTENSA)
int __pad;
#endif
abi_llong l_start;
diff --git a/linux-user/unicore32/syscall_nr.h b/linux-user/unicore32/syscall_nr.h
deleted file mode 100644
index 486b8c45a0..0000000000
--- a/linux-user/unicore32/syscall_nr.h
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * This file contains the system call numbers for UniCore32 oldabi.
- *
- * Copyright (C) 2010-2011 GUAN Xue-tao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#define TARGET_NR_restart_syscall 0
-#define TARGET_NR_exit 1
-#define TARGET_NR_fork 2
-#define TARGET_NR_read 3
-#define TARGET_NR_write 4
-#define TARGET_NR_open 5
-#define TARGET_NR_close 6
-#define TARGET_NR_waitpid 7
-#define TARGET_NR_creat 8
-#define TARGET_NR_link 9
-#define TARGET_NR_unlink 10
-#define TARGET_NR_execve 11
-#define TARGET_NR_chdir 12
-#define TARGET_NR_time 13
-#define TARGET_NR_mknod 14
-#define TARGET_NR_chmod 15
-#define TARGET_NR_lchown 16
-#define TARGET_NR_break 17
- /* 18 */
-#define TARGET_NR_lseek 19
-#define TARGET_NR_getpid 20
-#define TARGET_NR_mount 21
-#define TARGET_NR_umount 22
-#define TARGET_NR_setuid 23
-#define TARGET_NR_getuid 24
-#define TARGET_NR_stime 25
-#define TARGET_NR_ptrace 26
-#define TARGET_NR_alarm 27
- /* 28 */
-#define TARGET_NR_pause 29
-#define TARGET_NR_utime 30
-#define TARGET_NR_stty 31
-#define TARGET_NR_gtty 32
-#define TARGET_NR_access 33
-#define TARGET_NR_nice 34
-#define TARGET_NR_ftime 35
-#define TARGET_NR_sync 36
-#define TARGET_NR_kill 37
-#define TARGET_NR_rename 38
-#define TARGET_NR_mkdir 39
-#define TARGET_NR_rmdir 40
-#define TARGET_NR_dup 41
-#define TARGET_NR_pipe 42
-#define TARGET_NR_times 43
-#define TARGET_NR_prof 44
-#define TARGET_NR_brk 45
-#define TARGET_NR_setgid 46
-#define TARGET_NR_getgid 47
-#define TARGET_NR_signal 48
-#define TARGET_NR_geteuid 49
-#define TARGET_NR_getegid 50
-#define TARGET_NR_acct 51
-#define TARGET_NR_umount2 52
-#define TARGET_NR_lock 53
-#define TARGET_NR_ioctl 54
-#define TARGET_NR_fcntl 55
-#define TARGET_NR_mpx 56
-#define TARGET_NR_setpgid 57
-#define TARGET_NR_ulimit 58
- /* 59 */
-#define TARGET_NR_umask 60
-#define TARGET_NR_chroot 61
-#define TARGET_NR_ustat 62
-#define TARGET_NR_dup2 63
-#define TARGET_NR_getppid 64
-#define TARGET_NR_getpgrp 65
-#define TARGET_NR_setsid 66
-#define TARGET_NR_sigaction 67
-#define TARGET_NR_sgetmask 68
-#define TARGET_NR_ssetmask 69
-#define TARGET_NR_setreuid 70
-#define TARGET_NR_setregid 71
-#define TARGET_NR_sigsuspend 72
-#define TARGET_NR_sigpending 73
-#define TARGET_NR_sethostname 74
-#define TARGET_NR_setrlimit 75
-#define TARGET_NR_getrlimit 76
-#define TARGET_NR_getrusage 77
-#define TARGET_NR_gettimeofday 78
-#define TARGET_NR_settimeofday 79
-#define TARGET_NR_getgroups 80
-#define TARGET_NR_setgroups 81
-#define TARGET_NR_select 82
-#define TARGET_NR_symlink 83
- /* 84 */
-#define TARGET_NR_readlink 85
-#define TARGET_NR_uselib 86
-#define TARGET_NR_swapon 87
-#define TARGET_NR_reboot 88
-#define TARGET_NR_readdir 89
-#define TARGET_NR_mmap 90
-#define TARGET_NR_munmap 91
-#define TARGET_NR_truncate 92
-#define TARGET_NR_ftruncate 93
-#define TARGET_NR_fchmod 94
-#define TARGET_NR_fchown 95
-#define TARGET_NR_getpriority 96
-#define TARGET_NR_setpriority 97
-#define TARGET_NR_profil 98
-#define TARGET_NR_statfs 99
-#define TARGET_NR_fstatfs 100
-#define TARGET_NR_ioperm 101
-#define TARGET_NR_socketcall 102
-#define TARGET_NR_syslog 103
-#define TARGET_NR_setitimer 104
-#define TARGET_NR_getitimer 105
-#define TARGET_NR_stat 106
-#define TARGET_NR_lstat 107
-#define TARGET_NR_fstat 108
- /* 109 */
- /* 110 */
-#define TARGET_NR_vhangup 111
-#define TARGET_NR_idle 112
-#define TARGET_NR_syscall 113
-#define TARGET_NR_wait4 114
-#define TARGET_NR_swapoff 115
-#define TARGET_NR_sysinfo 116
-#define TARGET_NR_ipc 117
-#define TARGET_NR_fsync 118
-#define TARGET_NR_sigreturn 119
-#define TARGET_NR_clone 120
-#define TARGET_NR_setdomainname 121
-#define TARGET_NR_uname 122
-#define TARGET_NR_modify_ldt 123
-#define TARGET_NR_adjtimex 124
-#define TARGET_NR_mprotect 125
-#define TARGET_NR_sigprocmask 126
-#define TARGET_NR_create_module 127
-#define TARGET_NR_init_module 128
-#define TARGET_NR_delete_module 129
-#define TARGET_NR_get_kernel_syms 130
-#define TARGET_NR_quotactl 131
-#define TARGET_NR_getpgid 132
-#define TARGET_NR_fchdir 133
-#define TARGET_NR_bdflush 134
-#define TARGET_NR_sysfs 135
-#define TARGET_NR_personality 136
-#define TARGET_NR_afs_syscall 137
-#define TARGET_NR_setfsuid 138
-#define TARGET_NR_setfsgid 139
-#define TARGET_NR__llseek 140
-#define TARGET_NR_getdents 141
-#define TARGET_NR__newselect 142
-#define TARGET_NR_flock 143
-#define TARGET_NR_msync 144
-#define TARGET_NR_readv 145
-#define TARGET_NR_writev 146
-#define TARGET_NR_getsid 147
-#define TARGET_NR_fdatasync 148
-#define TARGET_NR__sysctl 149
-#define TARGET_NR_mlock 150
-#define TARGET_NR_munlock 151
-#define TARGET_NR_mlockall 152
-#define TARGET_NR_munlockall 153
-#define TARGET_NR_sched_setparam 154
-#define TARGET_NR_sched_getparam 155
-#define TARGET_NR_sched_setscheduler 156
-#define TARGET_NR_sched_getscheduler 157
-#define TARGET_NR_sched_yield 158
-#define TARGET_NR_sched_get_priority_max 159
-#define TARGET_NR_sched_get_priority_min 160
-#define TARGET_NR_sched_rr_get_interval 161
-#define TARGET_NR_nanosleep 162
-#define TARGET_NR_mremap 163
-#define TARGET_NR_setresuid 164
-#define TARGET_NR_getresuid 165
-#define TARGET_NR_vm86 166
-#define TARGET_NR_query_module 167
-#define TARGET_NR_poll 168
-#define TARGET_NR_nfsservctl 169
-#define TARGET_NR_setresgid 170
-#define TARGET_NR_getresgid 171
-#define TARGET_NR_prctl 172
-#define TARGET_NR_rt_sigreturn 173
-#define TARGET_NR_rt_sigaction 174
-#define TARGET_NR_rt_sigprocmask 175
-#define TARGET_NR_rt_sigpending 176
-#define TARGET_NR_rt_sigtimedwait 177
-#define TARGET_NR_rt_sigqueueinfo 178
-#define TARGET_NR_rt_sigsuspend 179
-#define TARGET_NR_pread64 180
-#define TARGET_NR_pwrite64 181
-#define TARGET_NR_chown 182
-#define TARGET_NR_getcwd 183
-#define TARGET_NR_capget 184
-#define TARGET_NR_capset 185
-#define TARGET_NR_sigaltstack 186
-#define TARGET_NR_sendfile 187
- /* 188 */
- /* 189 */
-#define TARGET_NR_vfork 190
-#define TARGET_NR_ugetrlimit 191
-#define TARGET_NR_mmap2 192
-#define TARGET_NR_truncate64 193
-#define TARGET_NR_ftruncate64 194
-#define TARGET_NR_stat64 195
-#define TARGET_NR_lstat64 196
-#define TARGET_NR_fstat64 197
-#define TARGET_NR_lchown32 198
-#define TARGET_NR_getuid32 199
-#define TARGET_NR_getgid32 200
-#define TARGET_NR_geteuid32 201
-#define TARGET_NR_getegid32 202
-#define TARGET_NR_setreuid32 203
-#define TARGET_NR_setregid32 204
-#define TARGET_NR_getgroups32 205
-#define TARGET_NR_setgroups32 206
-#define TARGET_NR_fchown32 207
-#define TARGET_NR_setresuid32 208
-#define TARGET_NR_getresuid32 209
-#define TARGET_NR_setresgid32 210
-#define TARGET_NR_getresgid32 211
-#define TARGET_NR_chown32 212
-#define TARGET_NR_setuid32 213
-#define TARGET_NR_setgid32 214
-#define TARGET_NR_setfsuid32 215
-#define TARGET_NR_setfsgid32 216
-#define TARGET_NR_getdents64 217
-#define TARGET_NR_pivot_root 218
-#define TARGET_NR_mincore 219
-#define TARGET_NR_madvise 220
-#define TARGET_NR_fcntl64 221
- /* 222 */
- /* 223 */
-#define TARGET_NR_gettid 224
-#define TARGET_NR_readahead 225
-#define TARGET_NR_setxattr 226
-#define TARGET_NR_lsetxattr 227
-#define TARGET_NR_fsetxattr 228
-#define TARGET_NR_getxattr 229
-#define TARGET_NR_lgetxattr 230
-#define TARGET_NR_fgetxattr 231
-#define TARGET_NR_listxattr 232
-#define TARGET_NR_llistxattr 233
-#define TARGET_NR_flistxattr 234
-#define TARGET_NR_removexattr 235
-#define TARGET_NR_lremovexattr 236
-#define TARGET_NR_fremovexattr 237
-#define TARGET_NR_tkill 238
-#define TARGET_NR_sendfile64 239
-#define TARGET_NR_futex 240
-#define TARGET_NR_sched_setaffinity 241
-#define TARGET_NR_sched_getaffinity 242
-#define TARGET_NR_io_setup 243
-#define TARGET_NR_io_destroy 244
-#define TARGET_NR_io_getevents 245
-#define TARGET_NR_io_submit 246
-#define TARGET_NR_io_cancel 247
-#define TARGET_NR_exit_group 248
-#define TARGET_NR_lookup_dcookie 249
-#define TARGET_NR_epoll_create 250
-#define TARGET_NR_epoll_ctl 251
-#define TARGET_NR_epoll_wait 252
-#define TARGET_NR_remap_file_pages 253
- /* 254 */
- /* 255 */
- /* 256 */
-#define TARGET_NR_set_tid_address 256
-#define TARGET_NR_timer_create 257
-#define TARGET_NR_timer_settime 258
-#define TARGET_NR_timer_gettime 259
-#define TARGET_NR_timer_getoverrun 260
-#define TARGET_NR_timer_delete 261
-#define TARGET_NR_clock_settime 262
-#define TARGET_NR_clock_gettime 263
-#define TARGET_NR_clock_getres 264
-#define TARGET_NR_clock_nanosleep 265
-#define TARGET_NR_statfs64 266
-#define TARGET_NR_fstatfs64 267
-#define TARGET_NR_tgkill 268
-#define TARGET_NR_utimes 269
-#define TARGET_NR_fadvise64_64 270
-#define TARGET_NR_pciconfig_iobase 271
-#define TARGET_NR_pciconfig_read 272
-#define TARGET_NR_pciconfig_write 273
-#define TARGET_NR_mq_open 274
-#define TARGET_NR_mq_unlink 275
-#define TARGET_NR_mq_timedsend 276
-#define TARGET_NR_mq_timedreceive 277
-#define TARGET_NR_mq_notify 278
-#define TARGET_NR_mq_getsetattr 279
-#define TARGET_NR_waitid 280
-#define TARGET_NR_socket 281
-#define TARGET_NR_bind 282
-#define TARGET_NR_connect 283
-#define TARGET_NR_listen 284
-#define TARGET_NR_accept 285
-#define TARGET_NR_getsockname 286
-#define TARGET_NR_getpeername 287
-#define TARGET_NR_socketpair 288
-#define TARGET_NR_send 289
-#define TARGET_NR_sendto 290
-#define TARGET_NR_recv 291
-#define TARGET_NR_recvfrom 292
-#define TARGET_NR_shutdown 293
-#define TARGET_NR_setsockopt 294
-#define TARGET_NR_getsockopt 295
-#define TARGET_NR_sendmsg 296
-#define TARGET_NR_recvmsg 297
-#define TARGET_NR_semop 298
-#define TARGET_NR_semget 299
-#define TARGET_NR_semctl 300
-#define TARGET_NR_msgsnd 301
-#define TARGET_NR_msgrcv 302
-#define TARGET_NR_msgget 303
-#define TARGET_NR_msgctl 304
-#define TARGET_NR_shmat 305
-#define TARGET_NR_shmdt 306
-#define TARGET_NR_shmget 307
-#define TARGET_NR_shmctl 308
-#define TARGET_NR_add_key 309
-#define TARGET_NR_request_key 310
-#define TARGET_NR_keyctl 311
-#define TARGET_NR_semtimedop 312
-#define TARGET_NR_vserver 313
-#define TARGET_NR_ioprio_set 314
-#define TARGET_NR_ioprio_get 315
-#define TARGET_NR_inotify_init 316
-#define TARGET_NR_inotify_add_watch 317
-#define TARGET_NR_inotify_rm_watch 318
-#define TARGET_NR_mbind 319
-#define TARGET_NR_get_mempolicy 320
-#define TARGET_NR_set_mempolicy 321
-#define TARGET_NR_openat 322
-#define TARGET_NR_mkdirat 323
-#define TARGET_NR_mknodat 324
-#define TARGET_NR_fchownat 325
-#define TARGET_NR_futimesat 326
-#define TARGET_NR_fstatat64 327
-#define TARGET_NR_unlinkat 328
-#define TARGET_NR_renameat 329
-#define TARGET_NR_linkat 330
-#define TARGET_NR_symlinkat 331
-#define TARGET_NR_readlinkat 332
-#define TARGET_NR_fchmodat 333
-#define TARGET_NR_faccessat 334
- /* 335 */
- /* 336 */
-#define TARGET_NR_unshare 337
-#define TARGET_NR_set_robust_list 338
-#define TARGET_NR_get_robust_list 339
-#define TARGET_NR_splice 340
-#define TARGET_NR_sync_file_range2 341
-#define TARGET_NR_tee 342
-#define TARGET_NR_vmsplice 343
-#define TARGET_NR_move_pages 344
-#define TARGET_NR_getcpu 345
- /* 346 */
-#define TARGET_NR_kexec_load 347
-#define TARGET_NR_utimensat 348
-#define TARGET_NR_signalfd 349
-#define TARGET_NR_timerfd 350
-#define TARGET_NR_eventfd 351
-#define TARGET_NR_fallocate 352
-#define TARGET_NR_timerfd_settime 353
-#define TARGET_NR_timerfd_gettime 354
-#define TARGET_NR_signalfd4 355
-#define TARGET_NR_eventfd2 356
-#define TARGET_NR_epoll_create1 357
-#define TARGET_NR_dup3 358
-#define TARGET_NR_pipe2 359
-#define TARGET_NR_inotify_init1 360
diff --git a/linux-user/unicore32/target_cpu.h b/linux-user/unicore32/target_cpu.h
deleted file mode 100644
index d7d2e7b083..0000000000
--- a/linux-user/unicore32/target_cpu.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * UniCore32 specific CPU ABI and functions for linux-user
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or (at your option) any
- * later version. See the COPYING file in the top-level directory.
- */
-#ifndef UNICORE32_TARGET_CPU_H
-#define UNICORE32_TARGET_CPU_H
-
-static inline void cpu_clone_regs(CPUUniCore32State *env, target_ulong newsp)
-{
- if (newsp) {
- env->regs[29] = newsp;
- }
- env->regs[0] = 0;
-}
-
-static inline void cpu_set_tls(CPUUniCore32State *env, target_ulong newtls)
-{
- env->regs[16] = newtls;
-}
-
-#endif
diff --git a/linux-user/unicore32/target_signal.h b/linux-user/unicore32/target_signal.h
deleted file mode 100644
index c6496fb9ea..0000000000
--- a/linux-user/unicore32/target_signal.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2010-2011 GUAN Xue-tao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef UNICORE32_TARGET_SIGNAL_H
-#define UNICORE32_TARGET_SIGNAL_H
-
-/* this struct defines a stack used during syscall handling */
-typedef struct target_sigaltstack {
- abi_ulong ss_sp;
- abi_ulong ss_flags;
- abi_ulong ss_size;
-} target_stack_t;
-
-/*
- * sigaltstack controls
- */
-#define TARGET_SS_ONSTACK 1
-#define TARGET_SS_DISABLE 2
-
-static inline abi_ulong get_sp_from_cpustate(CPUUniCore32State *state)
-{
- return state->regs[29];
-}
-
-
-#endif /* UNICORE32_TARGET_SIGNAL_H */
diff --git a/linux-user/unicore32/target_structs.h b/linux-user/unicore32/target_structs.h
deleted file mode 100644
index fbd4fa3f53..0000000000
--- a/linux-user/unicore32/target_structs.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * UniCore32 specific structures for linux-user
- *
- * Copyright (c) 2013 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 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef UNICORE32_TARGET_STRUCTS_H
-#define UNICORE32_TARGET_STRUCTS_H
-
-struct target_ipc_perm {
- abi_int __key; /* Key. */
- abi_uint uid; /* Owner's user ID. */
- abi_uint gid; /* Owner's group ID. */
- abi_uint cuid; /* Creator's user ID. */
- abi_uint cgid; /* Creator's group ID. */
- abi_ushort mode; /* Read/write permission. */
- abi_ushort __pad1;
- abi_ushort __seq; /* Sequence number. */
- abi_ushort __pad2;
- abi_ulong __unused1;
- abi_ulong __unused2;
-};
-
-struct target_shmid_ds {
- struct target_ipc_perm shm_perm; /* operation permission struct */
- abi_long shm_segsz; /* size of segment in bytes */
- abi_ulong shm_atime; /* time of last shmat() */
-#if TARGET_ABI_BITS == 32
- abi_ulong __unused1;
-#endif
- abi_ulong shm_dtime; /* time of last shmdt() */
-#if TARGET_ABI_BITS == 32
- abi_ulong __unused2;
-#endif
- abi_ulong shm_ctime; /* time of last change by shmctl() */
-#if TARGET_ABI_BITS == 32
- abi_ulong __unused3;
-#endif
- abi_int shm_cpid; /* pid of creator */
- abi_int shm_lpid; /* pid of last shmop */
- abi_ulong shm_nattch; /* number of current attaches */
- abi_ulong __unused4;
- abi_ulong __unused5;
-};
-
-#endif
diff --git a/linux-user/unicore32/target_syscall.h b/linux-user/unicore32/target_syscall.h
deleted file mode 100644
index 346b207700..0000000000
--- a/linux-user/unicore32/target_syscall.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010-2011 GUAN Xue-tao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef UNICORE32_TARGET_SYSCALL_H
-#define UNICORE32_TARGET_SYSCALL_H
-
-struct target_pt_regs {
- abi_ulong uregs[34];
-};
-
-#define UC32_REG_pc uregs[31]
-#define UC32_REG_lr uregs[30]
-#define UC32_REG_sp uregs[29]
-#define UC32_REG_ip uregs[28]
-#define UC32_REG_fp uregs[27]
-#define UC32_REG_26 uregs[26]
-#define UC32_REG_25 uregs[25]
-#define UC32_REG_24 uregs[24]
-#define UC32_REG_23 uregs[23]
-#define UC32_REG_22 uregs[22]
-#define UC32_REG_21 uregs[21]
-#define UC32_REG_20 uregs[20]
-#define UC32_REG_19 uregs[19]
-#define UC32_REG_18 uregs[18]
-#define UC32_REG_17 uregs[17]
-#define UC32_REG_16 uregs[16]
-#define UC32_REG_15 uregs[15]
-#define UC32_REG_14 uregs[14]
-#define UC32_REG_13 uregs[13]
-#define UC32_REG_12 uregs[12]
-#define UC32_REG_11 uregs[11]
-#define UC32_REG_10 uregs[10]
-#define UC32_REG_09 uregs[9]
-#define UC32_REG_08 uregs[8]
-#define UC32_REG_07 uregs[7]
-#define UC32_REG_06 uregs[6]
-#define UC32_REG_05 uregs[5]
-#define UC32_REG_04 uregs[4]
-#define UC32_REG_03 uregs[3]
-#define UC32_REG_02 uregs[2]
-#define UC32_REG_01 uregs[1]
-#define UC32_REG_00 uregs[0]
-#define UC32_REG_asr uregs[32]
-#define UC32_REG_ORIG_00 uregs[33]
-
-#define UC32_SYSCALL_BASE 0x900000
-#define UC32_SYSCALL_ARCH_BASE 0xf0000
-#define UC32_SYSCALL_NR_set_tls (UC32_SYSCALL_ARCH_BASE + 5)
-
-#define UNAME_MACHINE "UniCore-II"
-#define UNAME_MINIMUM_RELEASE "2.6.32"
-
-#define TARGET_MINSIGSTKSZ 2048
-#define TARGET_MLOCKALL_MCL_CURRENT 1
-#define TARGET_MLOCKALL_MCL_FUTURE 2
-
-#endif /* UNICORE32_TARGET_SYSCALL_H */
diff --git a/linux-user/unicore32/termbits.h b/linux-user/unicore32/termbits.h
deleted file mode 100644
index a5fcd64abf..0000000000
--- a/linux-user/unicore32/termbits.h
+++ /dev/null
@@ -1,2 +0,0 @@
-/* NOTE: exactly the same as i386 */
-#include "../i386/termbits.h"
diff --git a/linux-user/xtensa/syscall.h b/linux-user/xtensa/syscall.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/linux-user/xtensa/syscall.h
diff --git a/linux-user/xtensa/syscall_nr.h b/linux-user/xtensa/syscall_nr.h
new file mode 100644
index 0000000000..cd5ef45f84
--- /dev/null
+++ b/linux-user/xtensa/syscall_nr.h
@@ -0,0 +1,437 @@
+/*
+ * include/asm-xtensa/unistd.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2009 Tensilica Inc.
+ */
+
+#ifndef _XTENSA_UNISTD_H
+#define _XTENSA_UNISTD_H
+
+#define TARGET_NR_spill 0
+#define TARGET_NR_xtensa 1
+#define TARGET_NR_available4 2
+#define TARGET_NR_available5 3
+#define TARGET_NR_available6 4
+#define TARGET_NR_available7 5
+#define TARGET_NR_available8 6
+#define TARGET_NR_available9 7
+
+/* File Operations */
+
+#define TARGET_NR_open 8
+#define TARGET_NR_close 9
+#define TARGET_NR_dup 10
+#define TARGET_NR_dup2 11
+#define TARGET_NR_read 12
+#define TARGET_NR_write 13
+#define TARGET_NR_select 14
+#define TARGET_NR_lseek 15
+#define TARGET_NR_poll 16
+#define TARGET_NR__llseek 17
+#define TARGET_NR_epoll_wait 18
+#define TARGET_NR_epoll_ctl 19
+#define TARGET_NR_epoll_create 20
+#define TARGET_NR_creat 21
+#define TARGET_NR_truncate 22
+#define TARGET_NR_ftruncate 23
+#define TARGET_NR_readv 24
+#define TARGET_NR_writev 25
+#define TARGET_NR_fsync 26
+#define TARGET_NR_fdatasync 27
+#define TARGET_NR_truncate64 28
+#define TARGET_NR_ftruncate64 29
+#define TARGET_NR_pread64 30
+#define TARGET_NR_pwrite64 31
+
+#define TARGET_NR_link 32
+#define TARGET_NR_rename 33
+#define TARGET_NR_symlink 34
+#define TARGET_NR_readlink 35
+#define TARGET_NR_mknod 36
+#define TARGET_NR_pipe 37
+#define TARGET_NR_unlink 38
+#define TARGET_NR_rmdir 39
+
+#define TARGET_NR_mkdir 40
+#define TARGET_NR_chdir 41
+#define TARGET_NR_fchdir 42
+#define TARGET_NR_getcwd 43
+
+#define TARGET_NR_chmod 44
+#define TARGET_NR_chown 45
+#define TARGET_NR_stat 46
+#define TARGET_NR_stat64 47
+
+#define TARGET_NR_lchown 48
+#define TARGET_NR_lstat 49
+#define TARGET_NR_lstat64 50
+#define TARGET_NR_available51 51
+
+#define TARGET_NR_fchmod 52
+#define TARGET_NR_fchown 53
+#define TARGET_NR_fstat 54
+#define TARGET_NR_fstat64 55
+
+#define TARGET_NR_flock 56
+#define TARGET_NR_access 57
+#define TARGET_NR_umask 58
+#define TARGET_NR_getdents 59
+#define TARGET_NR_getdents64 60
+#define TARGET_NR_fcntl64 61
+#define TARGET_NR_fallocate 62
+#define TARGET_NR_fadvise64_64 63
+#define TARGET_NR_utime 64 /* glibc 2.3.3 ?? */
+#define TARGET_NR_utimes 65
+#define TARGET_NR_ioctl 66
+#define TARGET_NR_fcntl 67
+
+#define TARGET_NR_setxattr 68
+#define TARGET_NR_getxattr 69
+#define TARGET_NR_listxattr 70
+#define TARGET_NR_removexattr 71
+#define TARGET_NR_lsetxattr 72
+#define TARGET_NR_lgetxattr 73
+#define TARGET_NR_llistxattr 74
+#define TARGET_NR_lremovexattr 75
+#define TARGET_NR_fsetxattr 76
+#define TARGET_NR_fgetxattr 77
+#define TARGET_NR_flistxattr 78
+#define TARGET_NR_fremovexattr 79
+
+/* File Map / Shared Memory Operations */
+
+#define TARGET_NR_mmap2 80
+#define TARGET_NR_munmap 81
+#define TARGET_NR_mprotect 82
+#define TARGET_NR_brk 83
+#define TARGET_NR_mlock 84
+#define TARGET_NR_munlock 85
+#define TARGET_NR_mlockall 86
+#define TARGET_NR_munlockall 87
+#define TARGET_NR_mremap 88
+#define TARGET_NR_msync 89
+#define TARGET_NR_mincore 90
+#define TARGET_NR_madvise 91
+#define TARGET_NR_shmget 92
+#define TARGET_NR_shmat 93
+#define TARGET_NR_shmctl 94
+#define TARGET_NR_shmdt 95
+
+/* Socket Operations */
+
+#define TARGET_NR_socket 96
+#define TARGET_NR_setsockopt 97
+#define TARGET_NR_getsockopt 98
+#define TARGET_NR_shutdown 99
+
+#define TARGET_NR_bind 100
+#define TARGET_NR_connect 101
+#define TARGET_NR_listen 102
+#define TARGET_NR_accept 103
+
+#define TARGET_NR_getsockname 104
+#define TARGET_NR_getpeername 105
+#define TARGET_NR_sendmsg 106
+#define TARGET_NR_recvmsg 107
+#define TARGET_NR_send 108
+#define TARGET_NR_recv 109
+#define TARGET_NR_sendto 110
+#define TARGET_NR_recvfrom 111
+
+#define TARGET_NR_socketpair 112
+#define TARGET_NR_sendfile 113
+#define TARGET_NR_sendfile64 114
+#define TARGET_NR_sendmmsg 115
+
+/* Process Operations */
+
+#define TARGET_NR_clone 116
+#define TARGET_NR_execve 117
+#define TARGET_NR_exit 118
+#define TARGET_NR_exit_group 119
+#define TARGET_NR_getpid 120
+#define TARGET_NR_wait4 121
+#define TARGET_NR_waitid 122
+#define TARGET_NR_kill 123
+#define TARGET_NR_tkill 124
+#define TARGET_NR_tgkill 125
+#define TARGET_NR_set_tid_address 126
+#define TARGET_NR_gettid 127
+#define TARGET_NR_setsid 128
+#define TARGET_NR_getsid 129
+#define TARGET_NR_prctl 130
+#define TARGET_NR_personality 131
+#define TARGET_NR_getpriority 132
+#define TARGET_NR_setpriority 133
+#define TARGET_NR_setitimer 134
+#define TARGET_NR_getitimer 135
+#define TARGET_NR_setuid 136
+#define TARGET_NR_getuid 137
+#define TARGET_NR_setgid 138
+#define TARGET_NR_getgid 139
+#define TARGET_NR_geteuid 140
+#define TARGET_NR_getegid 141
+#define TARGET_NR_setreuid 142
+#define TARGET_NR_setregid 143
+#define TARGET_NR_setresuid 144
+#define TARGET_NR_getresuid 145
+#define TARGET_NR_setresgid 146
+#define TARGET_NR_getresgid 147
+#define TARGET_NR_setpgid 148
+#define TARGET_NR_getpgid 149
+#define TARGET_NR_getppid 150
+#define TARGET_NR_getpgrp 151
+
+#define TARGET_NR_reserved152 152 /* set_thread_area */
+#define TARGET_NR_reserved153 153 /* get_thread_area */
+#define TARGET_NR_times 154
+#define TARGET_NR_acct 155
+#define TARGET_NR_sched_setaffinity 156
+#define TARGET_NR_sched_getaffinity 157
+#define TARGET_NR_capget 158
+#define TARGET_NR_capset 159
+#define TARGET_NR_ptrace 160
+#define TARGET_NR_semtimedop 161
+#define TARGET_NR_semget 162
+#define TARGET_NR_semop 163
+#define TARGET_NR_semctl 164
+#define TARGET_NR_available165 165
+#define TARGET_NR_msgget 166
+#define TARGET_NR_msgsnd 167
+#define TARGET_NR_msgrcv 168
+#define TARGET_NR_msgctl 169
+#define TARGET_NR_available170 170
+
+/* File System */
+
+#define TARGET_NR_umount2 171
+#define TARGET_NR_mount 172
+#define TARGET_NR_swapon 173
+#define TARGET_NR_chroot 174
+#define TARGET_NR_pivot_root 175
+#define TARGET_NR_umount 176
+#define TARGET_NR_swapoff 177
+#define TARGET_NR_sync 178
+#define TARGET_NR_syncfs 179
+#define TARGET_NR_setfsuid 180
+#define TARGET_NR_setfsgid 181
+#define TARGET_NR_sysfs 182
+#define TARGET_NR_ustat 183
+#define TARGET_NR_statfs 184
+#define TARGET_NR_fstatfs 185
+#define TARGET_NR_statfs64 186
+#define TARGET_NR_fstatfs64 187
+
+/* System */
+
+#define TARGET_NR_setrlimit 188
+#define TARGET_NR_getrlimit 189
+#define TARGET_NR_getrusage 190
+#define TARGET_NR_futex 191
+#define TARGET_NR_gettimeofday 192
+#define TARGET_NR_settimeofday 193
+#define TARGET_NR_adjtimex 194
+#define TARGET_NR_nanosleep 195
+#define TARGET_NR_getgroups 196
+#define TARGET_NR_setgroups 197
+#define TARGET_NR_sethostname 198
+#define TARGET_NR_setdomainname 199
+#define TARGET_NR_syslog 200
+#define TARGET_NR_vhangup 201
+#define TARGET_NR_uselib 202
+#define TARGET_NR_reboot 203
+#define TARGET_NR_quotactl 204
+#define TARGET_NR_nfsservctl 205
+#define TARGET_NR__sysctl 206
+#define TARGET_NR_bdflush 207
+#define TARGET_NR_uname 208
+#define TARGET_NR_sysinfo 209
+#define TARGET_NR_init_module 210
+#define TARGET_NR_delete_module 211
+
+#define TARGET_NR_sched_setparam 212
+#define TARGET_NR_sched_getparam 213
+#define TARGET_NR_sched_setscheduler 214
+#define TARGET_NR_sched_getscheduler 215
+#define TARGET_NR_sched_get_priority_max 216
+#define TARGET_NR_sched_get_priority_min 217
+#define TARGET_NR_sched_rr_get_interval 218
+#define TARGET_NR_sched_yield 219
+#define TARGET_NR_available222 222
+
+/* Signal Handling */
+
+#define TARGET_NR_restart_syscall 223
+#define TARGET_NR_sigaltstack 224
+#define TARGET_NR_rt_sigreturn 225
+#define TARGET_NR_rt_sigaction 226
+#define TARGET_NR_rt_sigprocmask 227
+#define TARGET_NR_rt_sigpending 228
+#define TARGET_NR_rt_sigtimedwait 229
+#define TARGET_NR_rt_sigqueueinfo 230
+#define TARGET_NR_rt_sigsuspend 231
+
+/* Message */
+
+#define TARGET_NR_mq_open 232
+#define TARGET_NR_mq_unlink 233
+#define TARGET_NR_mq_timedsend 234
+#define TARGET_NR_mq_timedreceive 235
+#define TARGET_NR_mq_notify 236
+#define TARGET_NR_mq_getsetattr 237
+#define TARGET_NR_available238 238
+
+/* IO */
+
+#define TARGET_NR_io_setup 239
+#define TARGET_NR_io_destroy 240
+#define TARGET_NR_io_submit 241
+#define TARGET_NR_io_getevents 242
+#define TARGET_NR_io_cancel 243
+#define TARGET_NR_clock_settime 244
+#define TARGET_NR_clock_gettime 245
+#define TARGET_NR_clock_getres 246
+#define TARGET_NR_clock_nanosleep 247
+
+/* Timer */
+
+#define TARGET_NR_timer_create 248
+#define TARGET_NR_timer_delete 249
+#define TARGET_NR_timer_settime 250
+#define TARGET_NR_timer_gettime 251
+#define TARGET_NR_timer_getoverrun 252
+
+/* System */
+
+#define TARGET_NR_reserved253 253
+#define TARGET_NR_lookup_dcookie 254
+#define TARGET_NR_available255 255
+#define TARGET_NR_add_key 256
+#define TARGET_NR_request_key 257
+#define TARGET_NR_keyctl 258
+#define TARGET_NR_available259 259
+
+
+#define TARGET_NR_readahead 260
+#define TARGET_NR_remap_file_pages 261
+#define TARGET_NR_migrate_pages 262
+#define TARGET_NR_mbind 263
+#define TARGET_NR_get_mempolicy 264
+#define TARGET_NR_set_mempolicy 265
+#define TARGET_NR_unshare 266
+#define TARGET_NR_move_pages 267
+#define TARGET_NR_splice 268
+#define TARGET_NR_tee 269
+#define TARGET_NR_vmsplice 270
+#define TARGET_NR_available271 271
+
+#define TARGET_NR_pselect6 272
+#define TARGET_NR_ppoll 273
+#define TARGET_NR_epoll_pwait 274
+#define TARGET_NR_epoll_create1 275
+
+#define TARGET_NR_inotify_init 276
+#define TARGET_NR_inotify_add_watch 277
+#define TARGET_NR_inotify_rm_watch 278
+#define TARGET_NR_inotify_init1 279
+
+#define TARGET_NR_getcpu 280
+#define TARGET_NR_kexec_load 281
+
+#define TARGET_NR_ioprio_set 282
+#define TARGET_NR_ioprio_get 283
+
+#define TARGET_NR_set_robust_list 284
+#define TARGET_NR_get_robust_list 285
+#define TARGET_NR_available286 286
+#define TARGET_NR_available287 287
+
+/* Relative File Operations */
+
+#define TARGET_NR_openat 288
+#define TARGET_NR_mkdirat 289
+#define TARGET_NR_mknodat 290
+#define TARGET_NR_unlinkat 291
+#define TARGET_NR_renameat 292
+#define TARGET_NR_linkat 293
+#define TARGET_NR_symlinkat 294
+#define TARGET_NR_readlinkat 295
+#define TARGET_NR_utimensat 296
+#define TARGET_NR_fchownat 297
+#define TARGET_NR_futimesat 298
+#define TARGET_NR_fstatat64 299
+#define TARGET_NR_fchmodat 300
+#define TARGET_NR_faccessat 301
+#define TARGET_NR_available302 302
+#define TARGET_NR_available303 303
+
+#define TARGET_NR_signalfd 304
+/* 305 was TARGET_NR_timerfd */
+#define TARGET_NR_eventfd 306
+#define TARGET_NR_recvmmsg 307
+
+#define TARGET_NR_setns 308
+#define TARGET_NR_signalfd4 309
+#define TARGET_NR_dup3 310
+#define TARGET_NR_pipe2 311
+
+#define TARGET_NR_timerfd_create 312
+#define TARGET_NR_timerfd_settime 313
+#define TARGET_NR_timerfd_gettime 314
+#define TARGET_NR_available315 315
+
+#define TARGET_NR_eventfd2 316
+#define TARGET_NR_preadv 317
+#define TARGET_NR_pwritev 318
+#define TARGET_NR_available319 319
+
+#define TARGET_NR_fanotify_init 320
+#define TARGET_NR_fanotify_mark 321
+#define TARGET_NR_process_vm_readv 322
+#define TARGET_NR_process_vm_writev 323
+
+#define TARGET_NR_name_to_handle_at 324
+#define TARGET_NR_open_by_handle_at 325
+#define TARGET_NR_sync_file_range2 326
+#define TARGET_NR_perf_event_open 327
+
+#define TARGET_NR_rt_tgsigqueueinfo 328
+#define TARGET_NR_clock_adjtime 329
+#define TARGET_NR_prlimit64 330
+#define TARGET_NR_kcmp 331
+
+#define TARGET_NR_finit_module 332
+
+#define TARGET_NR_accept4 333
+
+#define TARGET_NR_sched_setattr 334
+#define TARGET_NR_sched_getattr 335
+
+#define TARGET_NR_renameat2 336
+
+#define TARGET_NR_seccomp 337
+#define TARGET_NR_getrandom 338
+#define TARGET_NR_memfd_create 339
+#define TARGET_NR_bpf 340
+#define TARGET_NR_execveat 341
+
+#define TARGET_NR_userfaultfd 342
+#define TARGET_NR_membarrier 343
+#define TARGET_NR_mlock2 344
+#define TARGET_NR_copy_file_range 345
+#define TARGET_NR_preadv2 346
+#define TARGET_NR_pwritev2 347
+
+#define TARGET_NR_pkey_mprotect 348
+#define TARGET_NR_pkey_alloc 349
+#define TARGET_NR_pkey_free 350
+
+#define TARGET_NR_statx 351
+
+#define TARGET_NR_syscall_count 352
+
+#endif /* _XTENSA_UNISTD_H */
diff --git a/linux-user/xtensa/target_cpu.h b/linux-user/xtensa/target_cpu.h
new file mode 100644
index 0000000000..747d828614
--- /dev/null
+++ b/linux-user/xtensa/target_cpu.h
@@ -0,0 +1,22 @@
+/*
+ * Xtensa-specific CPU ABI and functions for linux-user
+ */
+#ifndef XTENSA_TARGET_CPU_H
+#define XTENSA_TARGET_CPU_H
+
+static inline void cpu_clone_regs(CPUXtensaState *env, target_ulong newsp)
+{
+ if (newsp) {
+ env->regs[1] = newsp;
+ env->sregs[WINDOW_BASE] = 0;
+ env->sregs[WINDOW_START] = 0x1;
+ }
+ env->regs[2] = 0;
+}
+
+static inline void cpu_set_tls(CPUXtensaState *env, target_ulong newtls)
+{
+ env->uregs[THREADPTR] = newtls;
+}
+
+#endif
diff --git a/linux-user/unicore32/target_elf.h b/linux-user/xtensa/target_elf.h
index e2bfcb2ca3..a9a3fabd89 100644
--- a/linux-user/unicore32/target_elf.h
+++ b/linux-user/xtensa/target_elf.h
@@ -5,10 +5,12 @@
* later version. See the COPYING file in the top-level directory.
*/
-#ifndef UNICORE32_TARGET_ELF_H
-#define UNICORE32_TARGET_ELF_H
+#ifndef XTENSA_TARGET_ELF_H
+#define XTENSA_TARGET_ELF_H
+
static inline const char *cpu_get_model(uint32_t eflags)
{
- return "any";
+ return XTENSA_DEFAULT_CPU_MODEL;
}
+
#endif
diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h
new file mode 100644
index 0000000000..c6962e70af
--- /dev/null
+++ b/linux-user/xtensa/target_signal.h
@@ -0,0 +1,28 @@
+#ifndef XTENSA_TARGET_SIGNAL_H
+#define XTENSA_TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_int ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+/*
+ * sigaltstack controls
+ */
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_SIGSTKSZ 8192
+
+static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state)
+{
+ return state->regs[1];
+}
+
+#endif
diff --git a/linux-user/xtensa/target_structs.h b/linux-user/xtensa/target_structs.h
new file mode 100644
index 0000000000..020e20e242
--- /dev/null
+++ b/linux-user/xtensa/target_structs.h
@@ -0,0 +1,28 @@
+#ifndef XTENSA_TARGET_STRUCTS_T
+#define XTENSA_TARGET_STRUCTS_T
+
+struct target_ipc_perm {
+ abi_int __key; /* Key. */
+ abi_uint uid; /* Owner's user ID. */
+ abi_uint gid; /* Owner's group ID. */
+ abi_uint cuid; /* Creator's user ID. */
+ abi_uint cgid; /* Creator's group ID. */
+ abi_uint mode; /* Read/write permission. */
+ abi_ushort __seq; /* Sequence number. */
+};
+
+struct target_shmid_ds {
+ struct target_ipc_perm shm_perm; /* operation permission struct */
+ abi_int shm_segsz; /* size of segment in bytes */
+ abi_long shm_atime; /* time of last shmat() */
+ abi_long shm_dtime; /* time of last shmdt() */
+ abi_long shm_ctime; /* time of last change by shmctl() */
+ abi_ushort shm_cpid; /* pid of creator */
+ abi_ushort shm_lpid; /* pid of last shmop */
+ abi_ushort shm_nattch; /* number of current attaches */
+ abi_ushort shm_unused; /* compatibility */
+ abi_ulong __unused2;
+ abi_ulong __unused3;
+};
+
+#endif
diff --git a/linux-user/xtensa/target_syscall.h b/linux-user/xtensa/target_syscall.h
new file mode 100644
index 0000000000..3866dad849
--- /dev/null
+++ b/linux-user/xtensa/target_syscall.h
@@ -0,0 +1,49 @@
+#ifndef XTENSA_TARGET_SYSCALL_H
+#define XTENSA_TARGET_SYSCALL_H
+
+#define UNAME_MACHINE "xtensa"
+
+#define UNAME_MINIMUM_RELEASE "3.19"
+#define TARGET_CLONE_BACKWARDS
+
+#define MMAP_SHIFT TARGET_PAGE_BITS
+
+typedef uint32_t xtensa_reg_t;
+typedef struct {
+} xtregs_opt_t; /* TODO */
+
+struct target_pt_regs {
+ xtensa_reg_t pc; /* 4 */
+ xtensa_reg_t ps; /* 8 */
+ xtensa_reg_t depc; /* 12 */
+ xtensa_reg_t exccause; /* 16 */
+ xtensa_reg_t excvaddr; /* 20 */
+ xtensa_reg_t debugcause; /* 24 */
+ xtensa_reg_t wmask; /* 28 */
+ xtensa_reg_t lbeg; /* 32 */
+ xtensa_reg_t lend; /* 36 */
+ xtensa_reg_t lcount; /* 40 */
+ xtensa_reg_t sar; /* 44 */
+ xtensa_reg_t windowbase; /* 48 */
+ xtensa_reg_t windowstart; /* 52 */
+ xtensa_reg_t syscall; /* 56 */
+ xtensa_reg_t icountlevel; /* 60 */
+ xtensa_reg_t scompare1; /* 64 */
+ xtensa_reg_t threadptr; /* 68 */
+
+ /* Additional configurable registers that are used by the compiler. */
+ xtregs_opt_t xtregs_opt;
+
+ /* Make sure the areg field is 16 bytes aligned. */
+ int align[0] __attribute__ ((aligned(16)));
+
+ /* current register frame.
+ * Note: The ESF for kernel exceptions ends after 16 registers!
+ */
+ xtensa_reg_t areg[16];
+};
+
+#define TARGET_MLOCKALL_MCL_CURRENT 1
+#define TARGET_MLOCKALL_MCL_FUTURE 2
+
+#endif
diff --git a/linux-user/xtensa/termbits.h b/linux-user/xtensa/termbits.h
new file mode 100644
index 0000000000..eed8286de7
--- /dev/null
+++ b/linux-user/xtensa/termbits.h
@@ -0,0 +1,328 @@
+/*
+ * include/asm-xtensa/termbits.h
+ *
+ * Copied from SH.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ */
+
+#ifndef _XTENSA_TERMBITS_H
+#define _XTENSA_TERMBITS_H
+
+#include <linux/posix_types.h>
+
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+typedef unsigned int tcflag_t;
+
+#define TARGET_NCCS 19
+struct target_termios {
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[TARGET_NCCS]; /* control characters */
+};
+
+struct target_termios2 {
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[TARGET_NCCS]; /* control characters */
+ speed_t c_ispeed; /* input speed */
+ speed_t c_ospeed; /* output speed */
+};
+
+struct target_ktermios {
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[TARGET_NCCS]; /* control characters */
+ speed_t c_ispeed; /* input speed */
+ speed_t c_ospeed; /* output speed */
+};
+
+/* c_cc characters */
+
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* c_iflag bits */
+
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8 0040000
+
+/* c_oflag bits */
+
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_BOTHER 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_B500000 0010005
+#define TARGET_B576000 0010006
+#define TARGET_B921600 0010007
+#define TARGET_B1000000 0010010
+#define TARGET_B1152000 0010011
+#define TARGET_B1500000 0010012
+#define TARGET_B2000000 0010013
+#define TARGET_B2500000 0010014
+#define TARGET_B3000000 0010015
+#define TARGET_B3500000 0010016
+#define TARGET_B4000000 0010017
+#define TARGET_CIBAUD 002003600000 /* input baud rate */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+#define TARGET_IBSHIFT 16 /* Shift from CBAUD to CIBAUD */
+
+/* c_lflag bits */
+
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* tcflow() and TCXONC use these */
+
+#define TARGET_TCOOFF 0
+#define TARGET_TCOON 1
+#define TARGET_TCIOFF 2
+#define TARGET_TCION 3
+
+/* tcflush() and TCFLSH use these */
+
+#define TARGET_TCIFLUSH 0
+#define TARGET_TCOFLUSH 1
+#define TARGET_TCIOFLUSH 2
+
+/* tcsetattr uses these */
+
+#define TARGET_TCSANOW 0
+#define TARGET_TCSADRAIN 1
+#define TARGET_TCSAFLUSH 2
+
+/* from arch/xtensa/include/uapi/asm/ioctls.h */
+
+#define TARGET_FIOCLEX _IO('f', 1)
+#define TARGET_FIONCLEX _IO('f', 2)
+#define TARGET_FIOASYNC _IOW('f', 125, int)
+#define TARGET_FIONBIO _IOW('f', 126, int)
+#define TARGET_FIONREAD _IOR('f', 127, int)
+#define TARGET_TIOCINQ FIONREAD
+#define TARGET_FIOQSIZE _IOR('f', 128, loff_t)
+
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+
+#define TARGET_TCGETA 0x80127417 /* _IOR('t', 23, struct termio) */
+#define TARGET_TCSETA 0x40127418 /* _IOW('t', 24, struct termio) */
+#define TARGET_TCSETAW 0x40127419 /* _IOW('t', 25, struct termio) */
+#define TARGET_TCSETAF 0x4012741C /* _IOW('t', 28, struct termio) */
+
+#define TARGET_TCSBRK _IO('t', 29)
+#define TARGET_TCXONC _IO('t', 30)
+#define TARGET_TCFLSH _IO('t', 31)
+
+#define TARGET_TIOCSWINSZ 0x40087467 /* _IOW('t', 103, struct winsize) */
+#define TARGET_TIOCGWINSZ 0x80087468 /* _IOR('t', 104, struct winsize) */
+#define TARGET_TIOCSTART _IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP _IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ _IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCSPGRP _IOW('t', 118, int)
+#define TARGET_TIOCGPGRP _IOR('t', 119, int)
+
+#define TARGET_TIOCEXCL _IO('T', 12)
+#define TARGET_TIOCNXCL _IO('T', 13)
+#define TARGET_TIOCSCTTY _IO('T', 14)
+
+#define TARGET_TIOCSTI _IOW('T', 18, char)
+#define TARGET_TIOCMGET _IOR('T', 21, unsigned int)
+#define TARGET_TIOCMBIS _IOW('T', 22, unsigned int)
+#define TARGET_TIOCMBIC _IOW('T', 23, unsigned int)
+#define TARGET_TIOCMSET _IOW('T', 24, unsigned int)
+# define TARGET_TIOCM_LE 0x001
+# define TARGET_TIOCM_DTR 0x002
+# define TARGET_TIOCM_RTS 0x004
+# define TARGET_TIOCM_ST 0x008
+# define TARGET_TIOCM_SR 0x010
+# define TARGET_TIOCM_CTS 0x020
+# define TARGET_TIOCM_CAR 0x040
+# define TARGET_TIOCM_RNG 0x080
+# define TARGET_TIOCM_DSR 0x100
+# define TARGET_TIOCM_CD TIOCM_CAR
+# define TARGET_TIOCM_RI TIOCM_RNG
+
+#define TARGET_TIOCGSOFTCAR _IOR('T', 25, unsigned int)
+#define TARGET_TIOCSSOFTCAR _IOW('T', 26, unsigned int)
+#define TARGET_TIOCLINUX _IOW('T', 28, char)
+#define TARGET_TIOCCONS _IO('T', 29)
+#define TARGET_TIOCGSERIAL 0x803C541E /*_IOR('T', 30, struct serial_struct)*/
+#define TARGET_TIOCSSERIAL 0x403C541F /*_IOW('T', 31, struct serial_struct)*/
+#define TARGET_TIOCPKT _IOW('T', 32, int)
+# define TARGET_TIOCPKT_DATA 0
+# define TARGET_TIOCPKT_FLUSHREAD 1
+# define TARGET_TIOCPKT_FLUSHWRITE 2
+# define TARGET_TIOCPKT_STOP 4
+# define TARGET_TIOCPKT_START 8
+# define TARGET_TIOCPKT_NOSTOP 16
+# define TARGET_TIOCPKT_DOSTOP 32
+# define TARGET_TIOCPKT_IOCTL 64
+
+
+#define TARGET_TIOCNOTTY _IO('T', 34)
+#define TARGET_TIOCSETD _IOW('T', 35, int)
+#define TARGET_TIOCGETD _IOR('T', 36, int)
+#define TARGET_TCSBRKP _IOW('T', 37, int) /* Needed for POSIX tcsendbreak()*/
+#define TARGET_TIOCSBRK _IO('T', 39) /* BSD compatibility */
+#define TARGET_TIOCCBRK _IO('T', 40) /* BSD compatibility */
+#define TARGET_TIOCGSID _IOR('T', 41, pid_t) /* Return the session ID of FD*/
+#define TARGET_TCGETS2 _IOR('T', 42, struct termios2)
+#define TARGET_TCSETS2 _IOW('T', 43, struct termios2)
+#define TARGET_TCSETSW2 _IOW('T', 44, struct termios2)
+#define TARGET_TCSETSF2 _IOW('T', 45, struct termios2)
+#define TARGET_TIOCGRS485 _IOR('T', 46, struct serial_rs485)
+#define TARGET_TIOCSRS485 _IOWR('T', 47, struct serial_rs485)
+#define TARGET_TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TARGET_TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */
+#define TARGET_TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
+#define TARGET_TIOCVHANGUP _IO('T', 0x37)
+#define TARGET_TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */
+#define TARGET_TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */
+#define TARGET_TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */
+#define TARGET_TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */
+
+#define TARGET_TIOCSERCONFIG _IO('T', 83)
+#define TARGET_TIOCSERGWILD _IOR('T', 84, int)
+#define TARGET_TIOCSERSWILD _IOW('T', 85, int)
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR _IOR('T', 89, unsigned int) /* Get line status reg. */
+/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#define TARGET_TIOCSERGETMULTI 0x80a8545a /* Get multiport config */
+/* _IOR('T', 90, struct serial_multiport_struct) */
+#define TARGET_TIOCSERSETMULTI 0x40a8545b /* Set multiport config */
+/* _IOW('T', 91, struct serial_multiport_struct) */
+
+#define TARGET_TIOCMIWAIT _IO('T', 92) /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#endif /* _XTENSA_TERMBITS_H */
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 99e038024d..c83ec47ba8 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -6,6 +6,7 @@ common-obj-y += qemu-file.o global_state.o
common-obj-y += qemu-file-channel.o
common-obj-y += xbzrle.o postcopy-ram.o
common-obj-y += qjson.o
+common-obj-y += block-dirty-bitmap.o
common-obj-$(CONFIG_RDMA) += rdma.o
diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c
new file mode 100644
index 0000000000..dd04f102d8
--- /dev/null
+++ b/migration/block-dirty-bitmap.c
@@ -0,0 +1,746 @@
+/*
+ * Block dirty bitmap postcopy migration
+ *
+ * Copyright IBM, Corp. 2009
+ * Copyright (c) 2016-2017 Virtuozzo International GmbH. All rights reserved.
+ *
+ * Authors:
+ * Liran Schour <lirans@il.ibm.com>
+ * Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ * This file is derived from migration/block.c, so it's author and IBM copyright
+ * are here, although content is quite different.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ *
+ * ***
+ *
+ * Here postcopy migration of dirty bitmaps is realized. Only QMP-addressable
+ * bitmaps are migrated.
+ *
+ * Bitmap migration implies creating bitmap with the same name and granularity
+ * in destination QEMU. If the bitmap with the same name (for the same node)
+ * already exists on destination an error will be generated.
+ *
+ * format of migration:
+ *
+ * # Header (shared for different chunk types)
+ * 1, 2 or 4 bytes: flags (see qemu_{put,put}_flags)
+ * [ 1 byte: node name size ] \ flags & DEVICE_NAME
+ * [ n bytes: node name ] /
+ * [ 1 byte: bitmap name size ] \ flags & BITMAP_NAME
+ * [ n bytes: bitmap name ] /
+ *
+ * # Start of bitmap migration (flags & START)
+ * header
+ * be64: granularity
+ * 1 byte: bitmap flags (corresponds to BdrvDirtyBitmap)
+ * bit 0 - bitmap is enabled
+ * bit 1 - bitmap is persistent
+ * bit 2 - bitmap is autoloading
+ * bits 3-7 - reserved, must be zero
+ *
+ * # Complete of bitmap migration (flags & COMPLETE)
+ * header
+ *
+ * # Data chunk of bitmap migration
+ * header
+ * be64: start sector
+ * be32: number of sectors
+ * [ be64: buffer size ] \ ! (flags & ZEROES)
+ * [ n bytes: buffer ] /
+ *
+ * The last chunk in stream should contain flags & EOS. The chunk may skip
+ * device and/or bitmap names, assuming them to be the same with the previous
+ * chunk.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block.h"
+#include "block/block_int.h"
+#include "sysemu/block-backend.h"
+#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
+#include "migration/misc.h"
+#include "migration/migration.h"
+#include "migration/qemu-file.h"
+#include "migration/vmstate.h"
+#include "migration/register.h"
+#include "qemu/hbitmap.h"
+#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "trace.h"
+
+#define CHUNK_SIZE (1 << 10)
+
+/* Flags occupy one, two or four bytes (Big Endian). The size is determined as
+ * follows:
+ * in first (most significant) byte bit 8 is clear --> one byte
+ * in first byte bit 8 is set --> two or four bytes, depending on second
+ * byte:
+ * | in second byte bit 8 is clear --> two bytes
+ * | in second byte bit 8 is set --> four bytes
+ */
+#define DIRTY_BITMAP_MIG_FLAG_EOS 0x01
+#define DIRTY_BITMAP_MIG_FLAG_ZEROES 0x02
+#define DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME 0x04
+#define DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME 0x08
+#define DIRTY_BITMAP_MIG_FLAG_START 0x10
+#define DIRTY_BITMAP_MIG_FLAG_COMPLETE 0x20
+#define DIRTY_BITMAP_MIG_FLAG_BITS 0x40
+
+#define DIRTY_BITMAP_MIG_EXTRA_FLAGS 0x80
+
+#define DIRTY_BITMAP_MIG_START_FLAG_ENABLED 0x01
+#define DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT 0x02
+/* 0x04 was "AUTOLOAD" flags on elder versions, no it is ignored */
+#define DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK 0xf8
+
+typedef struct DirtyBitmapMigBitmapState {
+ /* Written during setup phase. */
+ BlockDriverState *bs;
+ const char *node_name;
+ BdrvDirtyBitmap *bitmap;
+ uint64_t total_sectors;
+ uint64_t sectors_per_chunk;
+ QSIMPLEQ_ENTRY(DirtyBitmapMigBitmapState) entry;
+ uint8_t flags;
+
+ /* For bulk phase. */
+ bool bulk_completed;
+ uint64_t cur_sector;
+} DirtyBitmapMigBitmapState;
+
+typedef struct DirtyBitmapMigState {
+ QSIMPLEQ_HEAD(dbms_list, DirtyBitmapMigBitmapState) dbms_list;
+
+ bool bulk_completed;
+ bool no_bitmaps;
+
+ /* for send_bitmap_bits() */
+ BlockDriverState *prev_bs;
+ BdrvDirtyBitmap *prev_bitmap;
+} DirtyBitmapMigState;
+
+typedef struct DirtyBitmapLoadState {
+ uint32_t flags;
+ char node_name[256];
+ char bitmap_name[256];
+ BlockDriverState *bs;
+ BdrvDirtyBitmap *bitmap;
+} DirtyBitmapLoadState;
+
+static DirtyBitmapMigState dirty_bitmap_mig_state;
+
+typedef struct DirtyBitmapLoadBitmapState {
+ BlockDriverState *bs;
+ BdrvDirtyBitmap *bitmap;
+ bool migrated;
+} DirtyBitmapLoadBitmapState;
+static GSList *enabled_bitmaps;
+QemuMutex finish_lock;
+
+void init_dirty_bitmap_incoming_migration(void)
+{
+ qemu_mutex_init(&finish_lock);
+}
+
+static uint32_t qemu_get_bitmap_flags(QEMUFile *f)
+{
+ uint8_t flags = qemu_get_byte(f);
+ if (flags & DIRTY_BITMAP_MIG_EXTRA_FLAGS) {
+ flags = flags << 8 | qemu_get_byte(f);
+ if (flags & DIRTY_BITMAP_MIG_EXTRA_FLAGS) {
+ flags = flags << 16 | qemu_get_be16(f);
+ }
+ }
+
+ return flags;
+}
+
+static void qemu_put_bitmap_flags(QEMUFile *f, uint32_t flags)
+{
+ /* The code currently do not send flags more than one byte */
+ assert(!(flags & (0xffffff00 | DIRTY_BITMAP_MIG_EXTRA_FLAGS)));
+
+ qemu_put_byte(f, flags);
+}
+
+static void send_bitmap_header(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
+ uint32_t additional_flags)
+{
+ BlockDriverState *bs = dbms->bs;
+ BdrvDirtyBitmap *bitmap = dbms->bitmap;
+ uint32_t flags = additional_flags;
+ trace_send_bitmap_header_enter();
+
+ if (bs != dirty_bitmap_mig_state.prev_bs) {
+ dirty_bitmap_mig_state.prev_bs = bs;
+ flags |= DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME;
+ }
+
+ if (bitmap != dirty_bitmap_mig_state.prev_bitmap) {
+ dirty_bitmap_mig_state.prev_bitmap = bitmap;
+ flags |= DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME;
+ }
+
+ qemu_put_bitmap_flags(f, flags);
+
+ if (flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) {
+ qemu_put_counted_string(f, dbms->node_name);
+ }
+
+ if (flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) {
+ qemu_put_counted_string(f, bdrv_dirty_bitmap_name(bitmap));
+ }
+}
+
+static void send_bitmap_start(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+{
+ send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_START);
+ qemu_put_be32(f, bdrv_dirty_bitmap_granularity(dbms->bitmap));
+ qemu_put_byte(f, dbms->flags);
+}
+
+static void send_bitmap_complete(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+{
+ send_bitmap_header(f, dbms, DIRTY_BITMAP_MIG_FLAG_COMPLETE);
+}
+
+static void send_bitmap_bits(QEMUFile *f, DirtyBitmapMigBitmapState *dbms,
+ uint64_t start_sector, uint32_t nr_sectors)
+{
+ /* align for buffer_is_zero() */
+ uint64_t align = 4 * sizeof(long);
+ uint64_t unaligned_size =
+ bdrv_dirty_bitmap_serialization_size(
+ dbms->bitmap, start_sector << BDRV_SECTOR_BITS,
+ (uint64_t)nr_sectors << BDRV_SECTOR_BITS);
+ uint64_t buf_size = QEMU_ALIGN_UP(unaligned_size, align);
+ uint8_t *buf = g_malloc0(buf_size);
+ uint32_t flags = DIRTY_BITMAP_MIG_FLAG_BITS;
+
+ bdrv_dirty_bitmap_serialize_part(
+ dbms->bitmap, buf, start_sector << BDRV_SECTOR_BITS,
+ (uint64_t)nr_sectors << BDRV_SECTOR_BITS);
+
+ if (buffer_is_zero(buf, buf_size)) {
+ g_free(buf);
+ buf = NULL;
+ flags |= DIRTY_BITMAP_MIG_FLAG_ZEROES;
+ }
+
+ trace_send_bitmap_bits(flags, start_sector, nr_sectors, buf_size);
+
+ send_bitmap_header(f, dbms, flags);
+
+ qemu_put_be64(f, start_sector);
+ qemu_put_be32(f, nr_sectors);
+
+ /* 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 & DIRTY_BITMAP_MIG_FLAG_ZEROES) {
+ qemu_fflush(f);
+ } else {
+ qemu_put_be64(f, buf_size);
+ qemu_put_buffer(f, buf, buf_size);
+ }
+
+ g_free(buf);
+}
+
+/* Called with iothread lock taken. */
+static void dirty_bitmap_mig_cleanup(void)
+{
+ DirtyBitmapMigBitmapState *dbms;
+
+ while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry);
+ bdrv_dirty_bitmap_set_qmp_locked(dbms->bitmap, false);
+ bdrv_unref(dbms->bs);
+ g_free(dbms);
+ }
+}
+
+/* Called with iothread lock taken. */
+static int init_dirty_bitmap_migration(void)
+{
+ BlockDriverState *bs;
+ BdrvDirtyBitmap *bitmap;
+ DirtyBitmapMigBitmapState *dbms;
+ BdrvNextIterator it;
+
+ dirty_bitmap_mig_state.bulk_completed = false;
+ dirty_bitmap_mig_state.prev_bs = NULL;
+ dirty_bitmap_mig_state.prev_bitmap = NULL;
+ dirty_bitmap_mig_state.no_bitmaps = false;
+
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
+ const char *drive_name = bdrv_get_device_or_node_name(bs);
+
+ /* skip automatically inserted nodes */
+ while (bs && bs->drv && bs->implicit) {
+ bs = backing_bs(bs);
+ }
+
+ for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap;
+ bitmap = bdrv_dirty_bitmap_next(bs, bitmap))
+ {
+ if (!bdrv_dirty_bitmap_name(bitmap)) {
+ continue;
+ }
+
+ if (drive_name == NULL) {
+ error_report("Found bitmap '%s' in unnamed node %p. It can't "
+ "be migrated", bdrv_dirty_bitmap_name(bitmap), bs);
+ goto fail;
+ }
+
+ if (bdrv_dirty_bitmap_frozen(bitmap)) {
+ error_report("Can't migrate frozen dirty bitmap: '%s",
+ bdrv_dirty_bitmap_name(bitmap));
+ goto fail;
+ }
+
+ if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
+ error_report("Can't migrate locked dirty bitmap: '%s",
+ bdrv_dirty_bitmap_name(bitmap));
+ goto fail;
+ }
+
+ bdrv_ref(bs);
+ bdrv_dirty_bitmap_set_qmp_locked(bitmap, true);
+
+ dbms = g_new0(DirtyBitmapMigBitmapState, 1);
+ dbms->bs = bs;
+ dbms->node_name = drive_name;
+ dbms->bitmap = bitmap;
+ dbms->total_sectors = bdrv_nb_sectors(bs);
+ dbms->sectors_per_chunk = CHUNK_SIZE * 8 *
+ bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS;
+ if (bdrv_dirty_bitmap_enabled(bitmap)) {
+ dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED;
+ }
+ if (bdrv_dirty_bitmap_get_persistance(bitmap)) {
+ dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT;
+ }
+
+ QSIMPLEQ_INSERT_TAIL(&dirty_bitmap_mig_state.dbms_list,
+ dbms, entry);
+ }
+ }
+
+ /* unset persistance here, to not roll back it */
+ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ bdrv_dirty_bitmap_set_persistance(dbms->bitmap, false);
+ }
+
+ if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) {
+ dirty_bitmap_mig_state.no_bitmaps = true;
+ }
+
+ return 0;
+
+fail:
+ dirty_bitmap_mig_cleanup();
+
+ return -1;
+}
+
+/* Called with no lock taken. */
+static void bulk_phase_send_chunk(QEMUFile *f, DirtyBitmapMigBitmapState *dbms)
+{
+ uint32_t nr_sectors = MIN(dbms->total_sectors - dbms->cur_sector,
+ dbms->sectors_per_chunk);
+
+ send_bitmap_bits(f, dbms, dbms->cur_sector, nr_sectors);
+
+ dbms->cur_sector += nr_sectors;
+ if (dbms->cur_sector >= dbms->total_sectors) {
+ dbms->bulk_completed = true;
+ }
+}
+
+/* Called with no lock taken. */
+static void bulk_phase(QEMUFile *f, bool limit)
+{
+ DirtyBitmapMigBitmapState *dbms;
+
+ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ while (!dbms->bulk_completed) {
+ bulk_phase_send_chunk(f, dbms);
+ if (limit && qemu_file_rate_limit(f)) {
+ return;
+ }
+ }
+ }
+
+ dirty_bitmap_mig_state.bulk_completed = true;
+}
+
+/* for SaveVMHandlers */
+static void dirty_bitmap_save_cleanup(void *opaque)
+{
+ dirty_bitmap_mig_cleanup();
+}
+
+static int dirty_bitmap_save_iterate(QEMUFile *f, void *opaque)
+{
+ trace_dirty_bitmap_save_iterate(migration_in_postcopy());
+
+ if (migration_in_postcopy() && !dirty_bitmap_mig_state.bulk_completed) {
+ bulk_phase(f, true);
+ }
+
+ qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
+
+ return dirty_bitmap_mig_state.bulk_completed;
+}
+
+/* Called with iothread lock taken. */
+
+static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque)
+{
+ DirtyBitmapMigBitmapState *dbms;
+ trace_dirty_bitmap_save_complete_enter();
+
+ if (!dirty_bitmap_mig_state.bulk_completed) {
+ bulk_phase(f, false);
+ }
+
+ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ send_bitmap_complete(f, dbms);
+ }
+
+ qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
+
+ trace_dirty_bitmap_save_complete_finish();
+
+ dirty_bitmap_mig_cleanup();
+ return 0;
+}
+
+static void dirty_bitmap_save_pending(QEMUFile *f, void *opaque,
+ uint64_t max_size,
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
+{
+ DirtyBitmapMigBitmapState *dbms;
+ uint64_t pending = 0;
+
+ qemu_mutex_lock_iothread();
+
+ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ uint64_t gran = bdrv_dirty_bitmap_granularity(dbms->bitmap);
+ uint64_t sectors = dbms->bulk_completed ? 0 :
+ dbms->total_sectors - dbms->cur_sector;
+
+ pending += DIV_ROUND_UP(sectors * BDRV_SECTOR_SIZE, gran);
+ }
+
+ qemu_mutex_unlock_iothread();
+
+ trace_dirty_bitmap_save_pending(pending, max_size);
+
+ *res_postcopy_only += pending;
+}
+
+/* First occurrence of this bitmap. It should be created if doesn't exist */
+static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s)
+{
+ Error *local_err = NULL;
+ uint32_t granularity = qemu_get_be32(f);
+ uint8_t flags = qemu_get_byte(f);
+
+ if (s->bitmap) {
+ error_report("Bitmap with the same name ('%s') already exists on "
+ "destination", bdrv_dirty_bitmap_name(s->bitmap));
+ return -EINVAL;
+ } else {
+ s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity,
+ s->bitmap_name, &local_err);
+ if (!s->bitmap) {
+ error_report_err(local_err);
+ return -EINVAL;
+ }
+ }
+
+ if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) {
+ error_report("Unknown flags in migrated dirty bitmap header: %x",
+ flags);
+ return -EINVAL;
+ }
+
+ if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) {
+ bdrv_dirty_bitmap_set_persistance(s->bitmap, true);
+ }
+
+ bdrv_disable_dirty_bitmap(s->bitmap);
+ if (flags & DIRTY_BITMAP_MIG_START_FLAG_ENABLED) {
+ DirtyBitmapLoadBitmapState *b;
+
+ bdrv_dirty_bitmap_create_successor(s->bs, s->bitmap, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return -EINVAL;
+ }
+
+ b = g_new(DirtyBitmapLoadBitmapState, 1);
+ b->bs = s->bs;
+ b->bitmap = s->bitmap;
+ b->migrated = false;
+ enabled_bitmaps = g_slist_prepend(enabled_bitmaps, b);
+ }
+
+ return 0;
+}
+
+void dirty_bitmap_mig_before_vm_start(void)
+{
+ GSList *item;
+
+ qemu_mutex_lock(&finish_lock);
+
+ for (item = enabled_bitmaps; item; item = g_slist_next(item)) {
+ DirtyBitmapLoadBitmapState *b = item->data;
+
+ if (b->migrated) {
+ bdrv_enable_dirty_bitmap(b->bitmap);
+ } else {
+ bdrv_dirty_bitmap_enable_successor(b->bitmap);
+ }
+
+ g_free(b);
+ }
+
+ g_slist_free(enabled_bitmaps);
+ enabled_bitmaps = NULL;
+
+ qemu_mutex_unlock(&finish_lock);
+}
+
+static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s)
+{
+ GSList *item;
+ trace_dirty_bitmap_load_complete();
+ bdrv_dirty_bitmap_deserialize_finish(s->bitmap);
+
+ qemu_mutex_lock(&finish_lock);
+
+ for (item = enabled_bitmaps; item; item = g_slist_next(item)) {
+ DirtyBitmapLoadBitmapState *b = item->data;
+
+ if (b->bitmap == s->bitmap) {
+ b->migrated = true;
+ break;
+ }
+ }
+
+ if (bdrv_dirty_bitmap_frozen(s->bitmap)) {
+ bdrv_dirty_bitmap_lock(s->bitmap);
+ if (enabled_bitmaps == NULL) {
+ /* in postcopy */
+ bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort);
+ bdrv_enable_dirty_bitmap(s->bitmap);
+ } else {
+ /* target not started, successor must be empty */
+ int64_t count = bdrv_get_dirty_count(s->bitmap);
+ BdrvDirtyBitmap *ret = bdrv_reclaim_dirty_bitmap_locked(s->bs,
+ s->bitmap,
+ NULL);
+ /* bdrv_reclaim_dirty_bitmap can fail only on no successor (it
+ * must be) or on merge fail, but merge can't fail when second
+ * bitmap is empty
+ */
+ assert(ret == s->bitmap &&
+ count == bdrv_get_dirty_count(s->bitmap));
+ }
+ bdrv_dirty_bitmap_unlock(s->bitmap);
+ }
+
+ qemu_mutex_unlock(&finish_lock);
+}
+
+static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s)
+{
+ uint64_t first_byte = qemu_get_be64(f) << BDRV_SECTOR_BITS;
+ uint64_t nr_bytes = (uint64_t)qemu_get_be32(f) << BDRV_SECTOR_BITS;
+ trace_dirty_bitmap_load_bits_enter(first_byte >> BDRV_SECTOR_BITS,
+ nr_bytes >> BDRV_SECTOR_BITS);
+
+ if (s->flags & DIRTY_BITMAP_MIG_FLAG_ZEROES) {
+ trace_dirty_bitmap_load_bits_zeroes();
+ bdrv_dirty_bitmap_deserialize_zeroes(s->bitmap, first_byte, nr_bytes,
+ false);
+ } else {
+ size_t ret;
+ uint8_t *buf;
+ uint64_t buf_size = qemu_get_be64(f);
+ uint64_t needed_size =
+ bdrv_dirty_bitmap_serialization_size(s->bitmap,
+ first_byte, nr_bytes);
+
+ if (needed_size > buf_size ||
+ buf_size > QEMU_ALIGN_UP(needed_size, 4 * sizeof(long))
+ /* Here used same alignment as in send_bitmap_bits */
+ ) {
+ error_report("Migrated bitmap granularity doesn't "
+ "match the destination bitmap '%s' granularity",
+ bdrv_dirty_bitmap_name(s->bitmap));
+ return -EINVAL;
+ }
+
+ buf = g_malloc(buf_size);
+ ret = qemu_get_buffer(f, buf, buf_size);
+ if (ret != buf_size) {
+ error_report("Failed to read bitmap bits");
+ return -EIO;
+ }
+
+ bdrv_dirty_bitmap_deserialize_part(s->bitmap, buf, first_byte, nr_bytes,
+ false);
+ g_free(buf);
+ }
+
+ return 0;
+}
+
+static int dirty_bitmap_load_header(QEMUFile *f, DirtyBitmapLoadState *s)
+{
+ Error *local_err = NULL;
+ bool nothing;
+ s->flags = qemu_get_bitmap_flags(f);
+ trace_dirty_bitmap_load_header(s->flags);
+
+ nothing = s->flags == (s->flags & DIRTY_BITMAP_MIG_FLAG_EOS);
+
+ if (s->flags & DIRTY_BITMAP_MIG_FLAG_DEVICE_NAME) {
+ if (!qemu_get_counted_string(f, s->node_name)) {
+ error_report("Unable to read node name string");
+ return -EINVAL;
+ }
+ s->bs = bdrv_lookup_bs(s->node_name, s->node_name, &local_err);
+ if (!s->bs) {
+ error_report_err(local_err);
+ return -EINVAL;
+ }
+ } else if (!s->bs && !nothing) {
+ error_report("Error: block device name is not set");
+ return -EINVAL;
+ }
+
+ if (s->flags & DIRTY_BITMAP_MIG_FLAG_BITMAP_NAME) {
+ if (!qemu_get_counted_string(f, s->bitmap_name)) {
+ error_report("Unable to read bitmap name string");
+ return -EINVAL;
+ }
+ s->bitmap = bdrv_find_dirty_bitmap(s->bs, s->bitmap_name);
+
+ /* bitmap may be NULL here, it wouldn't be an error if it is the
+ * first occurrence of the bitmap */
+ if (!s->bitmap && !(s->flags & DIRTY_BITMAP_MIG_FLAG_START)) {
+ error_report("Error: unknown dirty bitmap "
+ "'%s' for block device '%s'",
+ s->bitmap_name, s->node_name);
+ return -EINVAL;
+ }
+ } else if (!s->bitmap && !nothing) {
+ error_report("Error: block device name is not set");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id)
+{
+ static DirtyBitmapLoadState s;
+ int ret = 0;
+
+ trace_dirty_bitmap_load_enter();
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ do {
+ ret = dirty_bitmap_load_header(f, &s);
+
+ if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) {
+ ret = dirty_bitmap_load_start(f, &s);
+ } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_COMPLETE) {
+ dirty_bitmap_load_complete(f, &s);
+ } else if (s.flags & DIRTY_BITMAP_MIG_FLAG_BITS) {
+ ret = dirty_bitmap_load_bits(f, &s);
+ }
+
+ if (!ret) {
+ ret = qemu_file_get_error(f);
+ }
+
+ if (ret) {
+ return ret;
+ }
+ } while (!(s.flags & DIRTY_BITMAP_MIG_FLAG_EOS));
+
+ trace_dirty_bitmap_load_success();
+ return 0;
+}
+
+static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque)
+{
+ DirtyBitmapMigBitmapState *dbms = NULL;
+ if (init_dirty_bitmap_migration() < 0) {
+ return -1;
+ }
+
+ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) {
+ send_bitmap_start(f, dbms);
+ }
+ qemu_put_bitmap_flags(f, DIRTY_BITMAP_MIG_FLAG_EOS);
+
+ return 0;
+}
+
+static bool dirty_bitmap_is_active(void *opaque)
+{
+ return migrate_dirty_bitmaps() && !dirty_bitmap_mig_state.no_bitmaps;
+}
+
+static bool dirty_bitmap_is_active_iterate(void *opaque)
+{
+ return dirty_bitmap_is_active(opaque) && !runstate_is_running();
+}
+
+static bool dirty_bitmap_has_postcopy(void *opaque)
+{
+ return true;
+}
+
+static SaveVMHandlers savevm_dirty_bitmap_handlers = {
+ .save_setup = dirty_bitmap_save_setup,
+ .save_live_complete_postcopy = dirty_bitmap_save_complete,
+ .save_live_complete_precopy = dirty_bitmap_save_complete,
+ .has_postcopy = dirty_bitmap_has_postcopy,
+ .save_live_pending = dirty_bitmap_save_pending,
+ .save_live_iterate = dirty_bitmap_save_iterate,
+ .is_active_iterate = dirty_bitmap_is_active_iterate,
+ .load_state = dirty_bitmap_load,
+ .save_cleanup = dirty_bitmap_save_cleanup,
+ .is_active = dirty_bitmap_is_active,
+};
+
+void dirty_bitmap_mig_init(void)
+{
+ QSIMPLEQ_INIT(&dirty_bitmap_mig_state.dbms_list);
+
+ register_savevm_live(NULL, "dirty-bitmap", 0, 1,
+ &savevm_dirty_bitmap_handlers,
+ &dirty_bitmap_mig_state);
+}
diff --git a/migration/block.c b/migration/block.c
index 41b95d1dd8..5c03632257 100644
--- a/migration/block.c
+++ b/migration/block.c
@@ -864,8 +864,9 @@ static int block_save_complete(QEMUFile *f, void *opaque)
}
static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
- uint64_t *non_postcopiable_pending,
- uint64_t *postcopiable_pending)
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
{
/* Estimate pending number of bytes to send */
uint64_t pending;
@@ -886,7 +887,7 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
DPRINTF("Enter save live pending %" PRIu64 "\n", pending);
/* We don't do postcopy */
- *non_postcopiable_pending += pending;
+ *res_precopy_only += pending;
}
static int block_load(QEMUFile *f, void *opaque, int version_id)
diff --git a/migration/migration.c b/migration/migration.c
index 1f22f463d3..fc629e5965 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -159,6 +159,9 @@ MigrationIncomingState *migration_incoming_get_current(void)
sizeof(struct PostCopyFD));
qemu_mutex_init(&mis_current.rp_mutex);
qemu_event_init(&mis_current.main_thread_load_event, false);
+
+ init_dirty_bitmap_incoming_migration();
+
once = true;
}
return &mis_current;
@@ -326,6 +329,8 @@ static void process_incoming_migration_bh(void *opaque)
state, we need to obey autostart. Any other state is set with
runstate_set. */
+ dirty_bitmap_mig_before_vm_start();
+
if (!global_state_received() ||
global_state_get_runstate() == RUN_STATE_RUNNING) {
if (autostart) {
@@ -1028,7 +1033,7 @@ void qmp_migrate_start_postcopy(Error **errp)
{
MigrationState *s = migrate_get_current();
- if (!migrate_postcopy_ram()) {
+ if (!migrate_postcopy()) {
error_setg(errp, "Enable postcopy with migrate_set_capability before"
" the start of migration");
return;
@@ -1514,7 +1519,7 @@ bool migrate_postcopy_ram(void)
bool migrate_postcopy(void)
{
- return migrate_postcopy_ram();
+ return migrate_postcopy_ram() || migrate_dirty_bitmaps();
}
bool migrate_auto_converge(void)
@@ -1571,6 +1576,15 @@ int migrate_decompress_threads(void)
return s->parameters.decompress_threads;
}
+bool migrate_dirty_bitmaps(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->enabled_capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS];
+}
+
bool migrate_use_events(void)
{
MigrationState *s;
@@ -2248,20 +2262,20 @@ typedef enum {
*/
static MigIterateState migration_iteration_run(MigrationState *s)
{
- uint64_t pending_size, pend_post, pend_nonpost;
+ uint64_t pending_size, pend_pre, pend_compat, pend_post;
bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE;
- qemu_savevm_state_pending(s->to_dst_file, s->threshold_size,
- &pend_nonpost, &pend_post);
- pending_size = pend_nonpost + pend_post;
+ qemu_savevm_state_pending(s->to_dst_file, s->threshold_size, &pend_pre,
+ &pend_compat, &pend_post);
+ pending_size = pend_pre + pend_compat + pend_post;
trace_migrate_pending(pending_size, s->threshold_size,
- pend_post, pend_nonpost);
+ pend_pre, pend_compat, pend_post);
if (pending_size && pending_size >= s->threshold_size) {
/* Still a significant amount to transfer */
if (migrate_postcopy() && !in_postcopy &&
- pend_nonpost <= s->threshold_size &&
+ pend_pre <= s->threshold_size &&
atomic_read(&s->start_postcopy)) {
if (postcopy_start(s)) {
error_report("%s: postcopy failed to start", __func__);
diff --git a/migration/migration.h b/migration/migration.h
index 83dc36b57a..8d2f320c48 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -209,6 +209,7 @@ bool migrate_postcopy(void);
bool migrate_release_ram(void);
bool migrate_postcopy_ram(void);
bool migrate_zero_blocks(void);
+bool migrate_dirty_bitmaps(void);
bool migrate_auto_converge(void);
bool migrate_use_multifd(void);
@@ -238,4 +239,7 @@ void migrate_send_rp_pong(MigrationIncomingState *mis,
int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname,
ram_addr_t start, size_t len);
+void dirty_bitmap_mig_before_vm_start(void);
+void init_dirty_bitmap_incoming_migration(void);
+
#endif
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index 2ab2bf362d..e85f501f86 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -734,6 +734,19 @@ size_t qemu_get_counted_string(QEMUFile *f, char buf[256])
}
/*
+ * Put a string with one preceding byte containing its length. The length of
+ * the string should be less than 256.
+ */
+void qemu_put_counted_string(QEMUFile *f, const char *str)
+{
+ size_t len = strlen(str);
+
+ assert(len < 256);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (const uint8_t *)str, len);
+}
+
+/*
* Set the blocking state of the QEMUFile.
* Note: On some transports the OS only keeps a single blocking state for
* both directions, and thus changing the blocking on the main
diff --git a/migration/qemu-file.h b/migration/qemu-file.h
index aae4e5ed36..f4f356ab12 100644
--- a/migration/qemu-file.h
+++ b/migration/qemu-file.h
@@ -174,4 +174,6 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
ram_addr_t offset, size_t size,
uint64_t *bytes_sent);
+void qemu_put_counted_string(QEMUFile *f, const char *name);
+
#endif
diff --git a/migration/ram.c b/migration/ram.c
index 6ce7770b8c..0e90efa092 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -2375,8 +2375,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
}
static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
- uint64_t *non_postcopiable_pending,
- uint64_t *postcopiable_pending)
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
{
RAMState **temp = opaque;
RAMState *rs = *temp;
@@ -2396,9 +2397,9 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
if (migrate_postcopy_ram()) {
/* We can do postcopy, and all the data is postcopiable */
- *postcopiable_pending += remaining_size;
+ *res_compatible += remaining_size;
} else {
- *non_postcopiable_pending += remaining_size;
+ *res_precopy_only += remaining_size;
}
}
diff --git a/migration/savevm.c b/migration/savevm.c
index 305c3ceaf5..e2be02afe4 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -54,6 +54,7 @@
#include "qemu/cutils.h"
#include "io/channel-buffer.h"
#include "io/channel-file.h"
+#include "sysemu/replay.h"
#ifndef ETH_P_RARP
#define ETH_P_RARP 0x8035
@@ -1028,6 +1029,11 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy)
continue;
}
}
+ if (se->ops && se->ops->is_active_iterate) {
+ if (!se->ops->is_active_iterate(se->opaque)) {
+ continue;
+ }
+ }
/*
* In the postcopy phase, any device that doesn't know how to
* do postcopy should have saved it's state in the _complete
@@ -1220,13 +1226,15 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
* for units that can't do postcopy.
*/
void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
- uint64_t *res_non_postcopiable,
- uint64_t *res_postcopiable)
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
{
SaveStateEntry *se;
- *res_non_postcopiable = 0;
- *res_postcopiable = 0;
+ *res_precopy_only = 0;
+ *res_compatible = 0;
+ *res_postcopy_only = 0;
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
@@ -1239,7 +1247,8 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size,
}
}
se->ops->save_live_pending(f, se->opaque, threshold_size,
- res_non_postcopiable, res_postcopiable);
+ res_precopy_only, res_compatible,
+ res_postcopy_only);
}
}
@@ -1698,6 +1707,8 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
trace_loadvm_postcopy_handle_run_vmstart();
+ dirty_bitmap_mig_before_vm_start();
+
if (autostart) {
/* Hold onto your hats, starting the CPU */
vm_start();
@@ -2210,6 +2221,12 @@ int save_snapshot(const char *name, Error **errp)
struct tm tm;
AioContext *aio_context;
+ if (!replay_can_snapshot()) {
+ error_report("Record/replay does not allow making snapshot "
+ "right now. Try once more later.");
+ return ret;
+ }
+
if (!bdrv_all_can_snapshot(&bs)) {
error_setg(errp, "Device '%s' is writable but does not support "
"snapshots", bdrv_get_device_name(bs));
@@ -2401,6 +2418,12 @@ int load_snapshot(const char *name, Error **errp)
AioContext *aio_context;
MigrationIncomingState *mis = migration_incoming_get_current();
+ if (!replay_can_snapshot()) {
+ error_report("Record/replay does not allow loading snapshot "
+ "right now. Try once more later.");
+ return -EINVAL;
+ }
+
if (!bdrv_all_can_snapshot(&bs)) {
error_setg(errp,
"Device '%s' is writable but does not support snapshots",
diff --git a/migration/savevm.h b/migration/savevm.h
index 295c4a1f2c..cf4f0d37ca 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -38,8 +38,9 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f);
int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
bool inactivate_disks);
void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size,
- uint64_t *res_non_postcopiable,
- uint64_t *res_postcopiable);
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only);
void qemu_savevm_send_ping(QEMUFile *f, uint32_t value);
void qemu_savevm_send_open_return_path(QEMUFile *f);
int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len);
diff --git a/migration/trace-events b/migration/trace-events
index 1e353a317f..a180d7b008 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -86,7 +86,7 @@ migrate_fd_cleanup(void) ""
migrate_fd_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(uint64_t size, uint64_t max, uint64_t post, uint64_t nonpost) "pending size %" PRIu64 " max %" PRIu64 " (post=%" PRIu64 " nonpost=%" PRIu64 ")"
+migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")"
migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d"
migration_completion_file_err(void) ""
migration_completion_postcopy_end(void) ""
@@ -233,3 +233,17 @@ colo_vm_state_change(const char *old, const char *new) "Change '%s' => '%s'"
colo_send_message(const char *msg) "Send '%s' message"
colo_receive_message(const char *msg) "Receive '%s' message"
colo_failover_set_state(const char *new_state) "new state %s"
+
+# migration/block-dirty-bitmap.c
+send_bitmap_header_enter(void) ""
+send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uint64_t data_size) "flags: 0x%x, start_sector: %" PRIu64 ", nr_sectors: %" PRIu32 ", data_size: %" PRIu64
+dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d"
+dirty_bitmap_save_complete_enter(void) ""
+dirty_bitmap_save_complete_finish(void) ""
+dirty_bitmap_save_pending(uint64_t pending, uint64_t max_size) "pending %" PRIu64 " max: %" PRIu64
+dirty_bitmap_load_complete(void) ""
+dirty_bitmap_load_bits_enter(uint64_t first_sector, uint32_t nr_sectors) "chunk: %" PRIu64 " %" PRIu32
+dirty_bitmap_load_bits_zeroes(void) ""
+dirty_bitmap_load_header(uint32_t flags) "flags 0x%x"
+dirty_bitmap_load_enter(void) ""
+dirty_bitmap_load_success(void) ""
diff --git a/monitor.c b/monitor.c
index a4417f26cd..6ccd2fc089 100644
--- a/monitor.c
+++ b/monitor.c
@@ -35,6 +35,8 @@
#include "net/net.h"
#include "net/slirp.h"
#include "chardev/char-fe.h"
+#include "chardev/char-io.h"
+#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "sysemu/numa.h"
#include "monitor/monitor.h"
@@ -58,6 +60,7 @@
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
+#include "qapi/qmp/qlist.h"
#include "qom/object_interfaces.h"
#include "trace-root.h"
#include "trace/control.h"
@@ -79,6 +82,7 @@
#include "qapi/qapi-introspect.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
+#include "sysemu/iothread.h"
#include "qemu/cutils.h"
#if defined(TARGET_S390X)
@@ -168,6 +172,16 @@ typedef struct {
* mode.
*/
QmpCommandList *commands;
+ bool qmp_caps[QMP_CAPABILITY__MAX];
+ /*
+ * Protects qmp request/response queue. Please take monitor_lock
+ * first when used together.
+ */
+ QemuMutex qmp_queue_lock;
+ /* Input queue that holds all the parsed QMP requests */
+ GQueue *qmp_requests;
+ /* Output queue contains all the QMP responses in order */
+ GQueue *qmp_responses;
} MonitorQMP;
/*
@@ -190,9 +204,11 @@ struct Monitor {
CharBackend chr;
int reset_seen;
int flags;
- int suspend_cnt;
+ int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
+ bool use_io_thr;
+ /* We can't access guest memory when holding the lock */
QemuMutex out_lock;
QString *outbuf;
guint out_watch;
@@ -207,16 +223,25 @@ struct Monitor {
void *password_opaque;
mon_cmd_t *cmd_table;
QLIST_HEAD(,mon_fd_t) fds;
- QLIST_ENTRY(Monitor) entry;
+ QTAILQ_ENTRY(Monitor) entry;
};
+/* Let's add monitor global variables to this struct. */
+static struct {
+ IOThread *mon_iothread;
+ /* Bottom half to dispatch the requests received from IO thread */
+ QEMUBH *qmp_dispatcher_bh;
+ /* Bottom half to deliver the responses back to clients */
+ QEMUBH *qmp_respond_bh;
+} mon_global;
+
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
/* Protects mon_list, monitor_event_state. */
static QemuMutex monitor_lock;
-static QLIST_HEAD(mon_list, Monitor) mon_list;
+static QTAILQ_HEAD(mon_list, Monitor) mon_list;
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
static int mon_refcount;
@@ -241,6 +266,21 @@ static inline bool monitor_is_qmp(const Monitor *mon)
}
/**
+ * Whether @mon is using readline? Note: not all HMP monitors use
+ * readline, e.g., gdbserver has a non-interactive HMP monitor, so
+ * readline is not used there.
+ */
+static inline bool monitor_uses_readline(const Monitor *mon)
+{
+ return mon->flags & MONITOR_USE_READLINE;
+}
+
+static inline bool monitor_is_hmp_non_interactive(const Monitor *mon)
+{
+ return !monitor_is_qmp(mon) && !monitor_uses_readline(mon);
+}
+
+/**
* Is the current monitor, if any, a QMP monitor?
*/
bool monitor_cur_is_qmp(void)
@@ -382,7 +422,8 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...)
return 0;
}
-static void monitor_json_emitter(Monitor *mon, const QObject *data)
+static void monitor_json_emitter_raw(Monitor *mon,
+ QObject *data)
{
QString *json;
@@ -396,6 +437,71 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
QDECREF(json);
}
+static void monitor_json_emitter(Monitor *mon, QObject *data)
+{
+ if (mon->use_io_thr) {
+ /*
+ * If using IO thread, we need to queue the item so that IO
+ * thread will do the rest for us. Take refcount so that
+ * caller won't free the data (which will be finally freed in
+ * responder thread).
+ */
+ qobject_incref(data);
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ g_queue_push_tail(mon->qmp.qmp_responses, (void *)data);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qemu_bh_schedule(mon_global.qmp_respond_bh);
+ } else {
+ /*
+ * If not using monitor IO thread, then we are in main thread.
+ * Do the emission right away.
+ */
+ monitor_json_emitter_raw(mon, data);
+ }
+}
+
+struct QMPResponse {
+ Monitor *mon;
+ QObject *data;
+};
+typedef struct QMPResponse QMPResponse;
+
+/*
+ * Return one QMPResponse. The response is only valid if
+ * response.data is not NULL.
+ */
+static QMPResponse monitor_qmp_response_pop_one(void)
+{
+ Monitor *mon;
+ QObject *data = NULL;
+
+ qemu_mutex_lock(&monitor_lock);
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ data = g_queue_pop_head(mon->qmp.qmp_responses);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ if (data) {
+ break;
+ }
+ }
+ qemu_mutex_unlock(&monitor_lock);
+ return (QMPResponse) { .mon = mon, .data = data };
+}
+
+static void monitor_qmp_bh_responder(void *opaque)
+{
+ QMPResponse response;
+
+ while (true) {
+ response = monitor_qmp_response_pop_one();
+ if (!response.data) {
+ break;
+ }
+ monitor_json_emitter_raw(response.mon, response.data);
+ qobject_decref(response.data);
+ }
+}
+
static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
/* Limit guest-triggerable events to 1 per second */
[QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS },
@@ -417,7 +523,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
Monitor *mon;
trace_monitor_protocol_event_emit(event, qdict);
- QLIST_FOREACH(mon, &mon_list, entry) {
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
if (monitor_is_qmp(mon)
&& mon->qmp.commands != &qmp_cap_negotiation_commands) {
monitor_json_emitter(mon, QOBJECT(qdict));
@@ -447,7 +553,7 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp)
/* Unthrottled event */
monitor_qapi_event_emit(event, qdict);
} else {
- QDict *data = qobject_to_qdict(qdict_get(qdict, "data"));
+ QDict *data = qobject_to(QDict, qdict_get(qdict, "data"));
MonitorQAPIEventState key = { .event = event, .data = data };
evstate = g_hash_table_lookup(monitor_qapi_event_state, &key);
@@ -570,13 +676,19 @@ static void monitor_qapi_event_init(void)
static void handle_hmp_command(Monitor *mon, const char *cmdline);
-static void monitor_data_init(Monitor *mon)
+static void monitor_data_init(Monitor *mon, bool skip_flush,
+ bool use_io_thr)
{
memset(mon, 0, sizeof(Monitor));
qemu_mutex_init(&mon->out_lock);
+ qemu_mutex_init(&mon->qmp.qmp_queue_lock);
mon->outbuf = qstring_new();
/* Use *mon_cmds by default. */
mon->cmd_table = mon_cmds;
+ mon->skip_flush = skip_flush;
+ mon->use_io_thr = use_io_thr;
+ mon->qmp.qmp_requests = g_queue_new();
+ mon->qmp.qmp_responses = g_queue_new();
}
static void monitor_data_destroy(Monitor *mon)
@@ -589,6 +701,9 @@ static void monitor_data_destroy(Monitor *mon)
readline_free(mon->rs);
QDECREF(mon->outbuf);
qemu_mutex_destroy(&mon->out_lock);
+ qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
+ g_queue_free(mon->qmp.qmp_requests);
+ g_queue_free(mon->qmp.qmp_responses);
}
char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
@@ -597,8 +712,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
char *output = NULL;
Monitor *old_mon, hmp;
- monitor_data_init(&hmp);
- hmp.skip_flush = true;
+ monitor_data_init(&hmp, true, false);
old_mon = cur_mon;
cur_mon = &hmp;
@@ -956,7 +1070,7 @@ EventInfoList *qmp_query_events(Error **errp)
static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
Error **errp)
{
- *ret_data = qobject_from_json(qmp_schema_json, &error_abort);
+ *ret_data = qobject_from_qlit(&qmp_schema_qlit);
}
/*
@@ -983,6 +1097,9 @@ static void qmp_unregister_commands_hack(void)
#endif
#ifndef TARGET_I386
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
+ qmp_unregister_command(&qmp_commands, "query-sev");
+ qmp_unregister_command(&qmp_commands, "query-sev-launch-measure");
+ qmp_unregister_command(&qmp_commands, "query-sev-capabilities");
#endif
#ifndef TARGET_S390X
qmp_unregister_command(&qmp_commands, "dump-skeys");
@@ -1003,7 +1120,7 @@ static void qmp_unregister_commands_hack(void)
#endif
}
-void monitor_init_qmp_commands(void)
+static void monitor_init_qmp_commands(void)
{
/*
* Two command lists:
@@ -1029,8 +1146,90 @@ void monitor_init_qmp_commands(void)
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
}
-void qmp_qmp_capabilities(Error **errp)
+static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
+{
+ return mon->qmp.qmp_caps[cap];
+}
+
+static bool qmp_oob_enabled(Monitor *mon)
+{
+ return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB);
+}
+
+static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list,
+ Error **errp)
+{
+ for (; list; list = list->next) {
+ assert(list->value < QMP_CAPABILITY__MAX);
+ switch (list->value) {
+ case QMP_CAPABILITY_OOB:
+ if (!mon->use_io_thr) {
+ /*
+ * Out-Of-Band only works with monitors that are
+ * running on dedicated IOThread.
+ */
+ error_setg(errp, "This monitor does not support "
+ "Out-Of-Band (OOB)");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* This function should only be called after capabilities are checked. */
+static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list)
+{
+ for (; list; list = list->next) {
+ mon->qmp.qmp_caps[list->value] = true;
+ }
+}
+
+/*
+ * Return true if check successful, or false otherwise. When false is
+ * returned, detailed error will be in errp if provided.
+ */
+static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
+{
+ const char *command;
+ QmpCommand *cmd;
+
+ command = qdict_get_try_str(req, "execute");
+ if (!command) {
+ error_setg(errp, "Command field 'execute' missing");
+ return false;
+ }
+
+ cmd = qmp_find_command(mon->qmp.commands, command);
+ if (!cmd) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has not been found", command);
+ return false;
+ }
+
+ if (qmp_is_oob(req)) {
+ if (!qmp_oob_enabled(mon)) {
+ error_setg(errp, "Please enable Out-Of-Band first "
+ "for the session during capabilities negotiation");
+ return false;
+ }
+ if (!(cmd->options & QCO_ALLOW_OOB)) {
+ error_setg(errp, "The command %s does not support OOB",
+ command);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
+ Error **errp)
{
+ Error *local_err = NULL;
+
if (cur_mon->qmp.commands == &qmp_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
@@ -1038,6 +1237,21 @@ void qmp_qmp_capabilities(Error **errp)
return;
}
+ /* Enable QMP capabilities provided by the client if applicable. */
+ if (has_enable) {
+ qmp_caps_check(cur_mon, enable, &local_err);
+ if (local_err) {
+ /*
+ * Failed check on any of the capabilities will fail the
+ * entire command (and thus not apply any of the other
+ * capabilities that were also requested).
+ */
+ error_propagate(errp, local_err);
+ return;
+ }
+ qmp_caps_apply(cur_mon, enable);
+ }
+
cur_mon->qmp.commands = &qmp_commands;
}
@@ -3755,31 +3969,74 @@ static int monitor_can_read(void *opaque)
{
Monitor *mon = opaque;
- return (mon->suspend_cnt == 0) ? 1 : 0;
+ return !atomic_mb_read(&mon->suspend_cnt);
}
-static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
+/*
+ * 1. This function takes ownership of rsp, err, and id.
+ * 2. rsp, err, and id may be NULL.
+ * 3. If err != NULL then rsp must be NULL.
+ */
+static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
+ Error *err, QObject *id)
{
- QObject *req, *rsp = NULL, *id = NULL;
QDict *qdict = NULL;
- Monitor *mon = cur_mon;
- Error *err = NULL;
- req = json_parser_parse_err(tokens, NULL, &err);
- if (!req && !err) {
- /* json_parser_parse_err() sucks: can fail without setting @err */
- error_setg(&err, QERR_JSON_PARSING);
- }
if (err) {
- goto err_out;
+ assert(!rsp);
+ qdict = qdict_new();
+ qdict_put_obj(qdict, "error", qmp_build_error_object(err));
+ error_free(err);
+ rsp = QOBJECT(qdict);
+ }
+
+ if (rsp) {
+ if (id) {
+ /* This is for the qdict below. */
+ qobject_incref(id);
+ qdict_put_obj(qobject_to(QDict, rsp), "id", id);
+ }
+
+ monitor_json_emitter(mon, rsp);
}
- qdict = qobject_to_qdict(req);
- if (qdict) {
- id = qdict_get(qdict, "id");
- qobject_incref(id);
- qdict_del(qdict, "id");
- } /* else will fail qmp_dispatch() */
+ qobject_decref(id);
+ qobject_decref(rsp);
+}
+
+struct QMPRequest {
+ /* Owner of the request */
+ Monitor *mon;
+ /* "id" field of the request */
+ QObject *id;
+ /* Request object to be handled */
+ QObject *req;
+ /*
+ * Whether we need to resume the monitor afterward. This flag is
+ * used to emulate the old QMP server behavior that the current
+ * command must be completed before execution of the next one.
+ */
+ bool need_resume;
+};
+typedef struct QMPRequest QMPRequest;
+
+/*
+ * Dispatch one single QMP request. The function will free the req_obj
+ * and objects inside it before return.
+ */
+static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
+{
+ Monitor *mon, *old_mon;
+ QObject *req, *rsp = NULL, *id;
+ QDict *qdict = NULL;
+ bool need_resume;
+
+ req = req_obj->req;
+ mon = req_obj->mon;
+ id = req_obj->id;
+ need_resume = req_obj->need_resume;
+
+ g_free(req_obj);
if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
QString *req_json = qobject_to_json(req);
@@ -3787,10 +4044,15 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
QDECREF(req_json);
}
- rsp = qmp_dispatch(cur_mon->qmp.commands, req);
+ old_mon = cur_mon;
+ cur_mon = mon;
+
+ rsp = qmp_dispatch(mon->qmp.commands, req);
+
+ cur_mon = old_mon;
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
- qdict = qdict_get_qdict(qobject_to_qdict(rsp), "error");
+ qdict = qdict_get_qdict(qobject_to(QDict, rsp), "error");
if (qdict
&& !g_strcmp0(qdict_get_try_str(qdict, "class"),
QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
@@ -3801,37 +4063,171 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
}
}
-err_out:
- if (err) {
- qdict = qdict_new();
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
- rsp = QOBJECT(qdict);
+ /* Respond if necessary */
+ monitor_qmp_respond(mon, rsp, NULL, id);
+
+ /* This pairs with the monitor_suspend() in handle_qmp_command(). */
+ if (need_resume) {
+ monitor_resume(mon);
}
- if (rsp) {
- if (id) {
- qdict_put_obj(qobject_to_qdict(rsp), "id", id);
- id = NULL;
+ qobject_decref(req);
+}
+
+/*
+ * Pop one QMP request from monitor queues, return NULL if not found.
+ * We are using round-robin fashion to pop the request, to avoid
+ * processing commands only on a very busy monitor. To achieve that,
+ * when we process one request on a specific monitor, we put that
+ * monitor to the end of mon_list queue.
+ */
+static QMPRequest *monitor_qmp_requests_pop_one(void)
+{
+ QMPRequest *req_obj = NULL;
+ Monitor *mon;
+
+ qemu_mutex_lock(&monitor_lock);
+
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ req_obj = g_queue_pop_head(mon->qmp.qmp_requests);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ if (req_obj) {
+ break;
}
+ }
- monitor_json_emitter(mon, rsp);
+ if (req_obj) {
+ /*
+ * We found one request on the monitor. Degrade this monitor's
+ * priority to lowest by re-inserting it to end of queue.
+ */
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
}
- qobject_decref(id);
- qobject_decref(rsp);
- qobject_decref(req);
+ qemu_mutex_unlock(&monitor_lock);
+
+ return req_obj;
}
-static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
+static void monitor_qmp_bh_dispatcher(void *data)
{
- Monitor *old_mon = cur_mon;
+ QMPRequest *req_obj = monitor_qmp_requests_pop_one();
- cur_mon = opaque;
+ if (req_obj) {
+ trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
+ monitor_qmp_dispatch_one(req_obj);
+ /* Reschedule instead of looping so the main loop stays responsive */
+ qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ }
+}
- json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size);
+#define QMP_REQ_QUEUE_LEN_MAX (8)
- cur_mon = old_mon;
+static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
+{
+ QObject *req, *id = NULL;
+ QDict *qdict = NULL;
+ MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
+ Monitor *mon = container_of(mon_qmp, Monitor, qmp);
+ Error *err = NULL;
+ QMPRequest *req_obj;
+
+ req = json_parser_parse_err(tokens, NULL, &err);
+ if (!req && !err) {
+ /* json_parser_parse_err() sucks: can fail without setting @err */
+ error_setg(&err, QERR_JSON_PARSING);
+ }
+ if (err) {
+ goto err;
+ }
+
+ /* Check against the request in general layout */
+ qdict = qmp_dispatch_check_obj(req, &err);
+ if (!qdict) {
+ goto err;
+ }
+
+ /* Check against OOB specific */
+ if (!qmp_cmd_oob_check(mon, qdict, &err)) {
+ goto err;
+ }
+
+ id = qdict_get(qdict, "id");
+
+ /* When OOB is enabled, the "id" field is mandatory. */
+ if (qmp_oob_enabled(mon) && !id) {
+ error_setg(&err, "Out-Of-Band capability requires that "
+ "every command contains an 'id' field");
+ goto err;
+ }
+
+ qobject_incref(id);
+ qdict_del(qdict, "id");
+
+ req_obj = g_new0(QMPRequest, 1);
+ req_obj->mon = mon;
+ req_obj->id = id;
+ req_obj->req = req;
+ req_obj->need_resume = false;
+
+ if (qmp_is_oob(qdict)) {
+ /* Out-Of-Band (OOB) requests are executed directly in parser. */
+ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id)
+ ?: "");
+ monitor_qmp_dispatch_one(req_obj);
+ return;
+ }
+
+ /* Protect qmp_requests and fetching its length. */
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+
+ /*
+ * If OOB is not enabled on the current monitor, we'll emulate the
+ * old behavior that we won't process the current monitor any more
+ * until it has responded. This helps make sure that as long as
+ * OOB is not enabled, the server will never drop any command.
+ */
+ if (!qmp_oob_enabled(mon)) {
+ monitor_suspend(mon);
+ req_obj->need_resume = true;
+ } else {
+ /* Drop the request if queue is full. */
+ if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) {
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ qapi_event_send_command_dropped(id,
+ COMMAND_DROP_REASON_QUEUE_FULL,
+ &error_abort);
+ qobject_decref(id);
+ qobject_decref(req);
+ g_free(req_obj);
+ return;
+ }
+ }
+
+ /*
+ * Put the request to the end of queue so that requests will be
+ * handled in time order. Ownership for req_obj, req, id,
+ * etc. will be delivered to the handler side.
+ */
+ g_queue_push_tail(mon->qmp.qmp_requests, req_obj);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+
+ /* Kick the dispatcher routine */
+ qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ return;
+
+err:
+ monitor_qmp_respond(mon, NULL, err, NULL);
+ qobject_decref(req);
+}
+
+static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
+{
+ Monitor *mon = opaque;
+
+ json_message_parser_feed(&mon->qmp.parser, (const char *) buf, size);
}
static void monitor_read(void *opaque, const uint8_t *buf, int size)
@@ -3866,28 +4262,70 @@ static void monitor_command_cb(void *opaque, const char *cmdline,
int monitor_suspend(Monitor *mon)
{
- if (!mon->rs)
+ if (monitor_is_hmp_non_interactive(mon)) {
return -ENOTTY;
- mon->suspend_cnt++;
+ }
+
+ atomic_inc(&mon->suspend_cnt);
+
+ if (monitor_is_qmp(mon)) {
+ /*
+ * Kick iothread to make sure this takes effect. It'll be
+ * evaluated again in prepare() of the watch object.
+ */
+ aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ }
+
+ trace_monitor_suspend(mon, 1);
return 0;
}
void monitor_resume(Monitor *mon)
{
- if (!mon->rs)
+ if (monitor_is_hmp_non_interactive(mon)) {
return;
- if (--mon->suspend_cnt == 0)
- readline_show_prompt(mon->rs);
+ }
+
+ if (atomic_dec_fetch(&mon->suspend_cnt) == 0) {
+ if (monitor_is_qmp(mon)) {
+ /*
+ * For QMP monitors that are running in IOThread, let's
+ * kick the thread in case it's sleeping.
+ */
+ if (mon->use_io_thr) {
+ aio_notify(iothread_get_aio_context(mon_global.mon_iothread));
+ }
+ } else {
+ assert(mon->rs);
+ readline_show_prompt(mon->rs);
+ }
+ }
+ trace_monitor_suspend(mon, -1);
}
-static QObject *get_qmp_greeting(void)
+static QObject *get_qmp_greeting(Monitor *mon)
{
+ QList *cap_list = qlist_new();
QObject *ver = NULL;
+ QMPCapability cap;
qmp_marshal_query_version(NULL, &ver, NULL);
- return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []}}",
- ver);
+ for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
+ if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) {
+ /* Monitors that are not using IOThread won't support OOB */
+ continue;
+ }
+ qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap)));
+ }
+
+ return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
+ ver, cap_list);
+}
+
+static void monitor_qmp_caps_reset(Monitor *mon)
+{
+ memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps));
}
static void monitor_qmp_event(void *opaque, int event)
@@ -3898,7 +4336,8 @@ static void monitor_qmp_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
mon->qmp.commands = &qmp_cap_negotiation_commands;
- data = get_qmp_greeting();
+ monitor_qmp_caps_reset(mon);
+ data = get_qmp_greeting(mon);
monitor_json_emitter(mon, data);
qobject_decref(data);
mon_refcount++;
@@ -3926,19 +4365,19 @@ static void monitor_event(void *opaque, int event)
monitor_resume(mon);
monitor_flush(mon);
} else {
- mon->suspend_cnt = 0;
+ atomic_mb_set(&mon->suspend_cnt, 0);
}
break;
case CHR_EVENT_MUX_OUT:
if (mon->reset_seen) {
- if (mon->suspend_cnt == 0) {
+ if (atomic_mb_read(&mon->suspend_cnt) == 0) {
monitor_printf(mon, "\n");
}
monitor_flush(mon);
monitor_suspend(mon);
} else {
- mon->suspend_cnt++;
+ atomic_inc(&mon->suspend_cnt);
}
qemu_mutex_lock(&mon->out_lock);
mon->mux_out = 1;
@@ -3982,6 +4421,49 @@ static void sortcmdlist(void)
qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd);
}
+static GMainContext *monitor_get_io_context(void)
+{
+ return iothread_get_g_main_context(mon_global.mon_iothread);
+}
+
+static AioContext *monitor_get_aio_context(void)
+{
+ return iothread_get_aio_context(mon_global.mon_iothread);
+}
+
+static void monitor_iothread_init(void)
+{
+ mon_global.mon_iothread = iothread_create("mon_iothread",
+ &error_abort);
+
+ /*
+ * This MUST be on main loop thread since we have commands that
+ * have assumption to be run on main loop thread. It would be
+ * nice that one day we can remove this assumption in the future.
+ */
+ mon_global.qmp_dispatcher_bh = aio_bh_new(qemu_get_aio_context(),
+ monitor_qmp_bh_dispatcher,
+ NULL);
+
+ /*
+ * Unlike the dispatcher BH, this must be run on the monitor IO
+ * thread, so that monitors that are using IO thread will make
+ * sure read/write operations are all done on the IO thread.
+ */
+ mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(),
+ monitor_qmp_bh_responder,
+ NULL);
+}
+
+void monitor_init_globals(void)
+{
+ monitor_init_qmp_commands();
+ monitor_qapi_event_init();
+ sortcmdlist();
+ qemu_mutex_init(&monitor_lock);
+ monitor_iothread_init();
+}
+
/* These functions just adapt the readline interface in a typesafe way. We
* could cast function pointers but that discards compiler checks.
*/
@@ -4022,24 +4504,43 @@ void error_vprintf_unless_qmp(const char *fmt, va_list ap)
}
}
-static void __attribute__((constructor)) monitor_lock_init(void)
+static void monitor_list_append(Monitor *mon)
{
- qemu_mutex_init(&monitor_lock);
+ qemu_mutex_lock(&monitor_lock);
+ QTAILQ_INSERT_HEAD(&mon_list, mon, entry);
+ qemu_mutex_unlock(&monitor_lock);
}
-void monitor_init(Chardev *chr, int flags)
+static void monitor_qmp_setup_handlers_bh(void *opaque)
{
- static int is_first_init = 1;
- Monitor *mon;
-
- if (is_first_init) {
- monitor_qapi_event_init();
- sortcmdlist();
- is_first_init = 0;
+ Monitor *mon = opaque;
+ GMainContext *context;
+
+ if (mon->use_io_thr) {
+ /*
+ * When use_io_thr is set, we use the global shared dedicated
+ * IO thread for this monitor to handle input/output.
+ */
+ context = monitor_get_io_context();
+ /* We should have inited globals before reaching here. */
+ assert(context);
+ } else {
+ /* The default main loop, which is the main thread */
+ context = NULL;
}
- mon = g_malloc(sizeof(*mon));
- monitor_data_init(mon);
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
+ monitor_qmp_event, NULL, mon, context, true);
+ monitor_list_append(mon);
+}
+
+void monitor_init(Chardev *chr, int flags)
+{
+ Monitor *mon = g_malloc(sizeof(*mon));
+ /* Enable IOThread for QMPs that are not using MUX chardev backends. */
+ bool use_io_thr = (!CHARDEV_IS_MUX(chr)) && (flags & MONITOR_USE_CONTROL);
+
+ monitor_data_init(mon, false, use_io_thr);
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
mon->flags = flags;
@@ -4052,31 +4553,77 @@ void monitor_init(Chardev *chr, int flags)
}
if (monitor_is_qmp(mon)) {
- qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read,
- monitor_qmp_event, NULL, mon, NULL, true);
qemu_chr_fe_set_echo(&mon->chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
+ if (mon->use_io_thr) {
+ /*
+ * Make sure the old iowatch is gone. It's possible when
+ * e.g. the chardev is in client mode, with wait=on.
+ */
+ remove_fd_in_watch(chr);
+ /*
+ * We can't call qemu_chr_fe_set_handlers() directly here
+ * since during the procedure the chardev will be active
+ * and running in monitor iothread, while we'll still do
+ * something before returning from it, which is a possible
+ * race too. To avoid that, we just create a BH to setup
+ * the handlers.
+ */
+ aio_bh_schedule_oneshot(monitor_get_aio_context(),
+ monitor_qmp_setup_handlers_bh, mon);
+ /* We'll add this to mon_list in the BH when setup done */
+ return;
+ } else {
+ qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read,
+ monitor_qmp_read, monitor_qmp_event,
+ NULL, mon, NULL, true);
+ }
} else {
qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_read,
monitor_event, NULL, mon, NULL, true);
}
- qemu_mutex_lock(&monitor_lock);
- QLIST_INSERT_HEAD(&mon_list, mon, entry);
- qemu_mutex_unlock(&monitor_lock);
+ monitor_list_append(mon);
}
void monitor_cleanup(void)
{
Monitor *mon, *next;
+ /*
+ * We need to explicitly stop the iothread (but not destroy it),
+ * cleanup the monitor resources, then destroy the iothread since
+ * we need to unregister from chardev below in
+ * monitor_data_destroy(), and chardev is not thread-safe yet
+ */
+ iothread_stop(mon_global.mon_iothread);
+
+ /*
+ * After we have IOThread to send responses, it's possible that
+ * when we stop the IOThread there are still replies queued in the
+ * responder queue. Flush all of them. Note that even after this
+ * flush it's still possible that out buffer is not flushed.
+ * It'll be done in below monitor_flush() as the last resort.
+ */
+ monitor_qmp_bh_responder(NULL);
+
qemu_mutex_lock(&monitor_lock);
- QLIST_FOREACH_SAFE(mon, &mon_list, entry, next) {
- QLIST_REMOVE(mon, entry);
+ QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) {
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ monitor_flush(mon);
monitor_data_destroy(mon);
g_free(mon);
}
qemu_mutex_unlock(&monitor_lock);
+
+ /* QEMUBHs needs to be deleted before destroying the IOThread. */
+ qemu_bh_delete(mon_global.qmp_dispatcher_bh);
+ mon_global.qmp_dispatcher_bh = NULL;
+ qemu_bh_delete(mon_global.qmp_respond_bh);
+ mon_global.qmp_respond_bh = NULL;
+
+ iothread_destroy(mon_global.mon_iothread);
+ mon_global.mon_iothread = NULL;
}
QemuOptsList qemu_mon_opts = {
@@ -4103,6 +4650,24 @@ void qmp_rtc_reset_reinjection(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection");
}
+
+SevInfo *qmp_query_sev(Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "query-sev");
+ return NULL;
+}
+
+SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-launch-measure");
+ return NULL;
+}
+
+SevCapability *qmp_query_sev_capabilities(Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-capabilities");
+ return NULL;
+}
#endif
#ifndef TARGET_S390X
diff --git a/nbd/client.c b/nbd/client.c
index dcad23a053..9b9b7f0ea2 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -595,6 +595,111 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
return QIO_CHANNEL(tioc);
}
+/* nbd_negotiate_simple_meta_context:
+ * Set one meta context. Simple means that reply must contain zero (not
+ * negotiated) or one (negotiated) contexts. More contexts would be considered
+ * as a protocol error. It's also implied that meta-data query equals queried
+ * context name, so, if server replies with something different then @context,
+ * it considered as error too.
+ * return 1 for successful negotiation, context_id is set
+ * 0 if operation is unsupported,
+ * -1 with errp set for any other error
+ */
+static int nbd_negotiate_simple_meta_context(QIOChannel *ioc,
+ const char *export,
+ const char *context,
+ uint32_t *context_id,
+ Error **errp)
+{
+ int ret;
+ NBDOptionReply reply;
+ uint32_t received_id;
+ bool received;
+ uint32_t export_len = strlen(export);
+ uint32_t context_len = strlen(context);
+ uint32_t data_len = sizeof(export_len) + export_len +
+ sizeof(uint32_t) + /* number of queries */
+ sizeof(context_len) + context_len;
+ char *data = g_malloc(data_len);
+ char *p = data;
+
+ stl_be_p(p, export_len);
+ memcpy(p += sizeof(export_len), export, export_len);
+ stl_be_p(p += export_len, 1);
+ stl_be_p(p += sizeof(uint32_t), context_len);
+ memcpy(p += sizeof(context_len), context, context_len);
+
+ ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data,
+ errp);
+ g_free(data);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
+ errp) < 0)
+ {
+ return -1;
+ }
+
+ ret = nbd_handle_reply_err(ioc, &reply, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ if (reply.type == NBD_REP_META_CONTEXT) {
+ char *name;
+ size_t len;
+
+ if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) {
+ return -1;
+ }
+ be32_to_cpus(&received_id);
+
+ len = reply.length - sizeof(received_id);
+ name = g_malloc(len + 1);
+ if (nbd_read(ioc, name, len, errp) < 0) {
+ g_free(name);
+ return -1;
+ }
+ name[len] = '\0';
+ if (strcmp(context, name)) {
+ error_setg(errp, "Failed to negotiate meta context '%s', server "
+ "answered with different context '%s'", context,
+ name);
+ g_free(name);
+ return -1;
+ }
+ g_free(name);
+
+ received = true;
+
+ /* receive NBD_REP_ACK */
+ if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply,
+ errp) < 0)
+ {
+ return -1;
+ }
+
+ ret = nbd_handle_reply_err(ioc, &reply, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+
+ if (reply.type != NBD_REP_ACK) {
+ error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
+ reply.type, NBD_REP_ACK);
+ return -1;
+ }
+
+ if (received) {
+ *context_id = received_id;
+ return 1;
+ }
+
+ return 0;
+}
int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
QCryptoTLSCreds *tlscreds, const char *hostname,
@@ -606,10 +711,12 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
int rc;
bool zeroes = true;
bool structured_reply = info->structured_reply;
+ bool base_allocation = info->base_allocation;
trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : "<null>");
info->structured_reply = false;
+ info->base_allocation = false;
rc = -EINVAL;
if (outioc) {
@@ -700,6 +807,16 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name,
info->structured_reply = result == 1;
}
+ if (info->structured_reply && base_allocation) {
+ result = nbd_negotiate_simple_meta_context(
+ ioc, name, "base:allocation",
+ &info->meta_base_allocation_id, errp);
+ if (result < 0) {
+ goto fail;
+ }
+ info->base_allocation = result == 1;
+ }
+
/* Try NBD_OPT_GO first - if it works, we are done (it
* also gives us a good message if the server requires
* TLS). If it is not available, fall back to
diff --git a/nbd/server.c b/nbd/server.c
index e714bfe6a1..cea158913b 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -22,6 +22,8 @@
#include "trace.h"
#include "nbd-internal.h"
+#define NBD_META_ID_BASE_ALLOCATION 0
+
static int system_errno_to_nbd_errno(int err)
{
switch (err) {
@@ -82,6 +84,16 @@ struct NBDExport {
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
+/* NBDExportMetaContexts represents a list of contexts to be exported,
+ * as selected by NBD_OPT_SET_META_CONTEXT. Also used for
+ * NBD_OPT_LIST_META_CONTEXT. */
+typedef struct NBDExportMetaContexts {
+ char export_name[NBD_MAX_NAME_SIZE + 1];
+ bool valid; /* means that negotiation of the option finished without
+ errors */
+ bool base_allocation; /* export base:allocation context (block status) */
+} NBDExportMetaContexts;
+
struct NBDClient {
int refcount;
void (*close_fn)(NBDClient *client, bool negotiated);
@@ -102,6 +114,7 @@ struct NBDClient {
bool closing;
bool structured_reply;
+ NBDExportMetaContexts export_meta;
uint32_t opt; /* Current option being negotiated */
uint32_t optlen; /* remaining length of data in ioc for the option being
@@ -218,22 +231,46 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type,
/* Drop remainder of the current option, and send a reply with the
* given error type and message. Return -errno on read or write
* failure; or 0 if connection is still live. */
-static int GCC_FMT_ATTR(4, 5)
-nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
- const char *fmt, ...)
+static int GCC_FMT_ATTR(4, 0)
+nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp,
+ const char *fmt, va_list va)
{
int ret = nbd_drop(client->ioc, client->optlen, errp);
- va_list va;
client->optlen = 0;
if (!ret) {
- va_start(va, fmt);
ret = nbd_negotiate_send_rep_verr(client, type, errp, fmt, va);
- va_end(va);
}
return ret;
}
+static int GCC_FMT_ATTR(4, 5)
+nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
+ const char *fmt, ...)
+{
+ int ret;
+ va_list va;
+
+ va_start(va, fmt);
+ ret = nbd_opt_vdrop(client, type, errp, fmt, va);
+ va_end(va);
+
+ return ret;
+}
+
+static int GCC_FMT_ATTR(3, 4)
+nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...)
+{
+ int ret;
+ va_list va;
+
+ va_start(va, fmt);
+ ret = nbd_opt_vdrop(client, NBD_REP_ERR_INVALID, errp, fmt, va);
+ va_end(va);
+
+ return ret;
+}
+
/* Read size bytes from the unparsed payload of the current option.
* Return -errno on I/O error, 0 if option was completely handled by
* sending a reply about inconsistent lengths, or 1 on success. */
@@ -241,14 +278,70 @@ static int nbd_opt_read(NBDClient *client, void *buffer, size_t size,
Error **errp)
{
if (size > client->optlen) {
- return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
- "Inconsistent lengths in option %s",
- nbd_opt_lookup(client->opt));
+ return nbd_opt_invalid(client, errp,
+ "Inconsistent lengths in option %s",
+ nbd_opt_lookup(client->opt));
}
client->optlen -= size;
return qio_channel_read_all(client->ioc, buffer, size, errp) < 0 ? -EIO : 1;
}
+/* Drop size bytes from the unparsed payload of the current option.
+ * Return -errno on I/O error, 0 if option was completely handled by
+ * sending a reply about inconsistent lengths, or 1 on success. */
+static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp)
+{
+ if (size > client->optlen) {
+ return nbd_opt_invalid(client, errp,
+ "Inconsistent lengths in option %s",
+ nbd_opt_lookup(client->opt));
+ }
+ client->optlen -= size;
+ return nbd_drop(client->ioc, size, errp) < 0 ? -EIO : 1;
+}
+
+/* nbd_opt_read_name
+ *
+ * Read a string with the format:
+ * uint32_t len (<= NBD_MAX_NAME_SIZE)
+ * len bytes string (not 0-terminated)
+ *
+ * @name should be enough to store NBD_MAX_NAME_SIZE+1.
+ * If @length is non-null, it will be set to the actual string length.
+ *
+ * Return -errno on I/O error, 0 if option was completely handled by
+ * sending a reply about inconsistent lengths, or 1 on success.
+ */
+static int nbd_opt_read_name(NBDClient *client, char *name, uint32_t *length,
+ Error **errp)
+{
+ int ret;
+ uint32_t len;
+
+ ret = nbd_opt_read(client, &len, sizeof(len), errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ cpu_to_be32s(&len);
+
+ if (len > NBD_MAX_NAME_SIZE) {
+ return nbd_opt_invalid(client, errp,
+ "Invalid name length: %" PRIu32, len);
+ }
+
+ ret = nbd_opt_read(client, name, len, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ name[len] = '\0';
+
+ if (length) {
+ *length = len;
+ }
+
+ return 1;
+}
+
/* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload.
* Return -errno on error, 0 on success. */
static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp,
@@ -306,6 +399,12 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
}
+static void nbd_check_meta_export_name(NBDClient *client)
+{
+ client->export_meta.valid &= !strcmp(client->exp->name,
+ client->export_meta.export_name);
+}
+
/* Send a reply to NBD_OPT_EXPORT_NAME.
* Return -errno on error, 0 on success. */
static int nbd_negotiate_handle_export_name(NBDClient *client,
@@ -357,6 +456,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client,
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
nbd_export_get(client->exp);
+ nbd_check_meta_export_name(client);
return 0;
}
@@ -398,9 +498,8 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
int ret;
assert(client->optlen);
- ret = nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
- "option '%s' has unexpected length",
- nbd_opt_lookup(client->opt));
+ ret = nbd_opt_invalid(client, errp, "option '%s' has unexpected length",
+ nbd_opt_lookup(client->opt));
if (fatal && !ret) {
error_setg(errp, "option '%s' has unexpected length",
nbd_opt_lookup(client->opt));
@@ -432,20 +531,10 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
2 bytes: N, number of requests (can be 0)
N * 2 bytes: N requests
*/
- rc = nbd_opt_read(client, &namelen, sizeof(namelen), errp);
- if (rc <= 0) {
- return rc;
- }
- be32_to_cpus(&namelen);
- if (namelen >= sizeof(name)) {
- return nbd_opt_drop(client, NBD_REP_ERR_INVALID, errp,
- "name too long for qemu");
- }
- rc = nbd_opt_read(client, name, namelen, errp);
+ rc = nbd_opt_read_name(client, name, &namelen, errp);
if (rc <= 0) {
return rc;
}
- name[namelen] = '\0';
trace_nbd_negotiate_handle_export_name_request(name);
rc = nbd_opt_read(client, &requests, sizeof(requests), errp);
@@ -561,6 +650,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags,
client->exp = exp;
QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);
nbd_export_get(client->exp);
+ nbd_check_meta_export_name(client);
rc = 1;
}
return rc;
@@ -615,6 +705,189 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
return QIO_CHANNEL(tioc);
}
+/* nbd_negotiate_send_meta_context
+ *
+ * Send one chunk of reply to NBD_OPT_{LIST,SET}_META_CONTEXT
+ *
+ * For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead.
+ */
+static int nbd_negotiate_send_meta_context(NBDClient *client,
+ const char *context,
+ uint32_t context_id,
+ Error **errp)
+{
+ NBDOptionReplyMetaContext opt;
+ struct iovec iov[] = {
+ {.iov_base = &opt, .iov_len = sizeof(opt)},
+ {.iov_base = (void *)context, .iov_len = strlen(context)}
+ };
+
+ if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
+ context_id = 0;
+ }
+
+ set_be_option_rep(&opt.h, client->opt, NBD_REP_META_CONTEXT,
+ sizeof(opt) - sizeof(opt.h) + iov[1].iov_len);
+ stl_be_p(&opt.context_id, context_id);
+
+ return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0;
+}
+
+/* nbd_meta_base_query
+ *
+ * Handle query to 'base' namespace. For now, only base:allocation context is
+ * available in it. 'len' is the amount of text remaining to be read from
+ * the current name, after the 'base:' portion has been stripped.
+ *
+ * Return -errno on I/O error, 0 if option was completely handled by
+ * sending a reply about inconsistent lengths, or 1 on success. */
+static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta,
+ uint32_t len, Error **errp)
+{
+ int ret;
+ char query[sizeof("allocation") - 1];
+ size_t alen = strlen("allocation");
+
+ if (len == 0) {
+ if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
+ meta->base_allocation = true;
+ }
+ return 1;
+ }
+
+ if (len != alen) {
+ return nbd_opt_skip(client, len, errp);
+ }
+
+ ret = nbd_opt_read(client, query, len, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ if (strncmp(query, "allocation", alen) == 0) {
+ meta->base_allocation = true;
+ }
+
+ return 1;
+}
+
+/* nbd_negotiate_meta_query
+ *
+ * Parse namespace name and call corresponding function to parse body of the
+ * query.
+ *
+ * The only supported namespace now is 'base'.
+ *
+ * The function aims not wasting time and memory to read long unknown namespace
+ * names.
+ *
+ * Return -errno on I/O error, 0 if option was completely handled by
+ * sending a reply about inconsistent lengths, or 1 on success. */
+static int nbd_negotiate_meta_query(NBDClient *client,
+ NBDExportMetaContexts *meta, Error **errp)
+{
+ int ret;
+ char query[sizeof("base:") - 1];
+ size_t baselen = strlen("base:");
+ uint32_t len;
+
+ ret = nbd_opt_read(client, &len, sizeof(len), errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ cpu_to_be32s(&len);
+
+ /* The only supported namespace for now is 'base'. So query should start
+ * with 'base:'. Otherwise, we can ignore it and skip the remainder. */
+ if (len < baselen) {
+ return nbd_opt_skip(client, len, errp);
+ }
+
+ len -= baselen;
+ ret = nbd_opt_read(client, query, baselen, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ if (strncmp(query, "base:", baselen) != 0) {
+ return nbd_opt_skip(client, len, errp);
+ }
+
+ return nbd_meta_base_query(client, meta, len, errp);
+}
+
+/* nbd_negotiate_meta_queries
+ * Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT
+ *
+ * Return -errno on I/O error, or 0 if option was completely handled. */
+static int nbd_negotiate_meta_queries(NBDClient *client,
+ NBDExportMetaContexts *meta, Error **errp)
+{
+ int ret;
+ NBDExport *exp;
+ NBDExportMetaContexts local_meta;
+ uint32_t nb_queries;
+ int i;
+
+ if (!client->structured_reply) {
+ return nbd_opt_invalid(client, errp,
+ "request option '%s' when structured reply "
+ "is not negotiated",
+ nbd_opt_lookup(client->opt));
+ }
+
+ if (client->opt == NBD_OPT_LIST_META_CONTEXT) {
+ /* Only change the caller's meta on SET. */
+ meta = &local_meta;
+ }
+
+ memset(meta, 0, sizeof(*meta));
+
+ ret = nbd_opt_read_name(client, meta->export_name, NULL, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ exp = nbd_export_find(meta->export_name);
+ if (exp == NULL) {
+ return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp,
+ "export '%s' not present", meta->export_name);
+ }
+
+ ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ cpu_to_be32s(&nb_queries);
+
+ if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) {
+ /* enable all known contexts */
+ meta->base_allocation = true;
+ } else {
+ for (i = 0; i < nb_queries; ++i) {
+ ret = nbd_negotiate_meta_query(client, meta, errp);
+ if (ret <= 0) {
+ return ret;
+ }
+ }
+ }
+
+ if (meta->base_allocation) {
+ ret = nbd_negotiate_send_meta_context(client, "base:allocation",
+ NBD_META_ID_BASE_ALLOCATION,
+ errp);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
+ if (ret == 0) {
+ meta->valid = true;
+ }
+
+ return ret;
+}
+
/* nbd_negotiate_options
* Process all NBD_OPT_* client option commands, during fixed newstyle
* negotiation.
@@ -805,6 +1078,12 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags,
}
break;
+ case NBD_OPT_LIST_META_CONTEXT:
+ case NBD_OPT_SET_META_CONTEXT:
+ ret = nbd_negotiate_meta_queries(client, &client->export_meta,
+ errp);
+ break;
+
default:
ret = nbd_opt_drop(client, NBD_REP_ERR_UNSUP, errp,
"Unsupported option %" PRIu32 " (%s)",
@@ -1342,6 +1621,34 @@ static int coroutine_fn nbd_co_send_structured_read(NBDClient *client,
return nbd_co_send_iov(client, iov, 2, errp);
}
+static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
+ uint64_t handle,
+ uint32_t error,
+ const char *msg,
+ Error **errp)
+{
+ NBDStructuredError chunk;
+ int nbd_err = system_errno_to_nbd_errno(error);
+ struct iovec iov[] = {
+ {.iov_base = &chunk, .iov_len = sizeof(chunk)},
+ {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0},
+ };
+
+ assert(nbd_err);
+ trace_nbd_co_send_structured_error(handle, nbd_err,
+ nbd_err_lookup(nbd_err), msg ? msg : "");
+ set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle,
+ sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
+ stl_be_p(&chunk.error, nbd_err);
+ stw_be_p(&chunk.message_length, iov[1].iov_len);
+
+ return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp);
+}
+
+/* Do a sparse read and send the structured reply to the client.
+ * Returns -errno if sending fails. bdrv_block_status_above() failure is
+ * reported to the client, at which point this function succeeds.
+ */
static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
uint64_t handle,
uint64_t offset,
@@ -1362,8 +1669,13 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
bool final;
if (status < 0) {
- error_setg_errno(errp, -status, "unable to check for holes");
- return status;
+ char *msg = g_strdup_printf("unable to check for holes: %s",
+ strerror(-status));
+
+ ret = nbd_co_send_structured_error(client, handle, -status, msg,
+ errp);
+ g_free(msg);
+ return ret;
}
assert(pnum && pnum <= size - progress);
final = progress + pnum == size;
@@ -1401,28 +1713,77 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
return ret;
}
-static int coroutine_fn nbd_co_send_structured_error(NBDClient *client,
- uint64_t handle,
- uint32_t error,
- const char *msg,
- Error **errp)
+static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, NBDExtent *extent)
{
- NBDStructuredError chunk;
- int nbd_err = system_errno_to_nbd_errno(error);
+ uint64_t remaining_bytes = bytes;
+
+ while (remaining_bytes) {
+ uint32_t flags;
+ int64_t num;
+ int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
+ &num, NULL, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
+ (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
+
+ if (remaining_bytes == bytes) {
+ extent->flags = flags;
+ }
+
+ if (flags != extent->flags) {
+ break;
+ }
+
+ offset += num;
+ remaining_bytes -= num;
+ }
+
+ cpu_to_be32s(&extent->flags);
+ extent->length = cpu_to_be32(bytes - remaining_bytes);
+
+ return 0;
+}
+
+/* nbd_co_send_extents
+ * @extents should be in big-endian */
+static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
+ NBDExtent *extents, unsigned nb_extents,
+ uint32_t context_id, Error **errp)
+{
+ NBDStructuredMeta chunk;
+
struct iovec iov[] = {
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
- {.iov_base = (char *)msg, .iov_len = msg ? strlen(msg) : 0},
+ {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
};
- assert(nbd_err);
- trace_nbd_co_send_structured_error(handle, nbd_err,
- nbd_err_lookup(nbd_err), msg ? msg : "");
- set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_ERROR, handle,
- sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
- stl_be_p(&chunk.error, nbd_err);
- stw_be_p(&chunk.message_length, iov[1].iov_len);
+ set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS,
+ handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
+ stl_be_p(&chunk.context_id, context_id);
- return nbd_co_send_iov(client, iov, 1 + !!iov[1].iov_len, errp);
+ return nbd_co_send_iov(client, iov, 2, errp);
+}
+
+/* Get block status from the exported device and send it to the client */
+static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
+ BlockDriverState *bs, uint64_t offset,
+ uint64_t length, uint32_t context_id,
+ Error **errp)
+{
+ int ret;
+ NBDExtent extent;
+
+ ret = blockstatus_to_extent_be(bs, offset, length, &extent);
+ if (ret < 0) {
+ return nbd_co_send_structured_error(
+ client, handle, -ret, "can't get block status", errp);
+ }
+
+ return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp);
}
/* nbd_co_receive_request
@@ -1502,6 +1863,8 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
valid_flags |= NBD_CMD_FLAG_DF;
} else if (request->type == NBD_CMD_WRITE_ZEROES) {
valid_flags |= NBD_CMD_FLAG_NO_HOLE;
+ } else if (request->type == NBD_CMD_BLOCK_STATUS) {
+ valid_flags |= NBD_CMD_FLAG_REQ_ONE;
}
if (request->flags & ~valid_flags) {
error_setg(errp, "unsupported flags for command %s (got 0x%x)",
@@ -1512,159 +1875,195 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request,
return 0;
}
-/* Owns a reference to the NBDClient passed as opaque. */
-static coroutine_fn void nbd_trip(void *opaque)
+/* Send simple reply without a payload, or a structured error
+ * @error_msg is ignored if @ret >= 0
+ * Returns 0 if connection is still live, -errno on failure to talk to client
+ */
+static coroutine_fn int nbd_send_generic_reply(NBDClient *client,
+ uint64_t handle,
+ int ret,
+ const char *error_msg,
+ Error **errp)
+{
+ if (client->structured_reply && ret < 0) {
+ return nbd_co_send_structured_error(client, handle, -ret, error_msg,
+ errp);
+ } else {
+ return nbd_co_send_simple_reply(client, handle, ret < 0 ? -ret : 0,
+ NULL, 0, errp);
+ }
+}
+
+/* Handle NBD_CMD_READ request.
+ * Return -errno if sending fails. Other errors are reported directly to the
+ * client as an error reply. */
+static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request,
+ uint8_t *data, Error **errp)
{
- NBDClient *client = opaque;
- NBDExport *exp = client->exp;
- NBDRequestData *req;
- NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
int ret;
- int flags;
- int reply_data_len = 0;
- Error *local_err = NULL;
- char *msg = NULL;
+ NBDExport *exp = client->exp;
- trace_nbd_trip();
- if (client->closing) {
- nbd_client_put(client);
- return;
- }
+ assert(request->type == NBD_CMD_READ);
- req = nbd_request_get(client);
- ret = nbd_co_receive_request(req, &request, &local_err);
- client->recv_coroutine = NULL;
- nbd_client_receive_next_request(client);
- if (ret == -EIO) {
- goto disconnect;
+ /* XXX: NBD Protocol only documents use of FUA with WRITE */
+ if (request->flags & NBD_CMD_FLAG_FUA) {
+ ret = blk_co_flush(exp->blk);
+ if (ret < 0) {
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "flush failed", errp);
+ }
}
- if (ret < 0) {
- goto reply;
+ if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) &&
+ request->len) {
+ return nbd_co_send_sparse_read(client, request->handle, request->from,
+ data, request->len, errp);
}
- if (client->closing) {
- /*
- * The client may be closed when we are blocked in
- * nbd_co_receive_request()
- */
- goto done;
+ ret = blk_pread(exp->blk, request->from + exp->dev_offset, data,
+ request->len);
+ if (ret < 0) {
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "reading from file failed", errp);
}
- switch (request.type) {
- case NBD_CMD_READ:
- /* XXX: NBD Protocol only documents use of FUA with WRITE */
- if (request.flags & NBD_CMD_FLAG_FUA) {
- ret = blk_co_flush(exp->blk);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "flush failed");
- break;
- }
- }
-
- if (client->structured_reply && !(request.flags & NBD_CMD_FLAG_DF) &&
- request.len) {
- ret = nbd_co_send_sparse_read(req->client, request.handle,
- request.from, req->data, request.len,
- &local_err);
- if (ret < 0) {
- goto reply;
- }
- goto done;
+ if (client->structured_reply) {
+ if (request->len) {
+ return nbd_co_send_structured_read(client, request->handle,
+ request->from, data,
+ request->len, true, errp);
+ } else {
+ return nbd_co_send_structured_done(client, request->handle, errp);
}
+ } else {
+ return nbd_co_send_simple_reply(client, request->handle, 0,
+ data, request->len, errp);
+ }
+}
- ret = blk_pread(exp->blk, request.from + exp->dev_offset,
- req->data, request.len);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "reading from file failed");
- break;
- }
+/* Handle NBD request.
+ * Return -errno if sending fails. Other errors are reported directly to the
+ * client as an error reply. */
+static coroutine_fn int nbd_handle_request(NBDClient *client,
+ NBDRequest *request,
+ uint8_t *data, Error **errp)
+{
+ int ret;
+ int flags;
+ NBDExport *exp = client->exp;
+ char *msg;
- reply_data_len = request.len;
+ switch (request->type) {
+ case NBD_CMD_READ:
+ return nbd_do_cmd_read(client, request, data, errp);
- break;
case NBD_CMD_WRITE:
flags = 0;
- if (request.flags & NBD_CMD_FLAG_FUA) {
+ if (request->flags & NBD_CMD_FLAG_FUA) {
flags |= BDRV_REQ_FUA;
}
- ret = blk_pwrite(exp->blk, request.from + exp->dev_offset,
- req->data, request.len, flags);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "writing to file failed");
- }
+ ret = blk_pwrite(exp->blk, request->from + exp->dev_offset,
+ data, request->len, flags);
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "writing to file failed", errp);
- break;
case NBD_CMD_WRITE_ZEROES:
flags = 0;
- if (request.flags & NBD_CMD_FLAG_FUA) {
+ if (request->flags & NBD_CMD_FLAG_FUA) {
flags |= BDRV_REQ_FUA;
}
- if (!(request.flags & NBD_CMD_FLAG_NO_HOLE)) {
+ if (!(request->flags & NBD_CMD_FLAG_NO_HOLE)) {
flags |= BDRV_REQ_MAY_UNMAP;
}
- ret = blk_pwrite_zeroes(exp->blk, request.from + exp->dev_offset,
- request.len, flags);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "writing to file failed");
- }
+ ret = blk_pwrite_zeroes(exp->blk, request->from + exp->dev_offset,
+ request->len, flags);
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "writing to file failed", errp);
- break;
case NBD_CMD_DISC:
/* unreachable, thanks to special case in nbd_co_receive_request() */
abort();
case NBD_CMD_FLUSH:
ret = blk_co_flush(exp->blk);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "flush failed");
- }
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "flush failed", errp);
- break;
case NBD_CMD_TRIM:
- ret = blk_co_pdiscard(exp->blk, request.from + exp->dev_offset,
- request.len);
- if (ret < 0) {
- error_setg_errno(&local_err, -ret, "discard failed");
+ ret = blk_co_pdiscard(exp->blk, request->from + exp->dev_offset,
+ request->len);
+ if (ret == 0 && request->flags & NBD_CMD_FLAG_FUA) {
+ ret = blk_co_flush(exp->blk);
+ }
+ return nbd_send_generic_reply(client, request->handle, ret,
+ "discard failed", errp);
+
+ case NBD_CMD_BLOCK_STATUS:
+ if (client->export_meta.valid && client->export_meta.base_allocation) {
+ return nbd_co_send_block_status(client, request->handle,
+ blk_bs(exp->blk), request->from,
+ request->len,
+ NBD_META_ID_BASE_ALLOCATION, errp);
+ } else {
+ return nbd_send_generic_reply(client, request->handle, -EINVAL,
+ "CMD_BLOCK_STATUS not negotiated",
+ errp);
}
- break;
default:
- error_setg(&local_err, "invalid request type (%" PRIu32 ") received",
- request.type);
- ret = -EINVAL;
+ msg = g_strdup_printf("invalid request type (%" PRIu32 ") received",
+ request->type);
+ ret = nbd_send_generic_reply(client, request->handle, -EINVAL, msg,
+ errp);
+ g_free(msg);
+ return ret;
}
+}
-reply:
- if (local_err) {
- /* If we get here, local_err was not a fatal error, and should be sent
- * to the client. */
- assert(ret < 0);
- msg = g_strdup(error_get_pretty(local_err));
- error_report_err(local_err);
- local_err = NULL;
+/* Owns a reference to the NBDClient passed as opaque. */
+static coroutine_fn void nbd_trip(void *opaque)
+{
+ NBDClient *client = opaque;
+ NBDRequestData *req;
+ NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
+ int ret;
+ Error *local_err = NULL;
+
+ trace_nbd_trip();
+ if (client->closing) {
+ nbd_client_put(client);
+ return;
}
- if (client->structured_reply &&
- (ret < 0 || request.type == NBD_CMD_READ)) {
- if (ret < 0) {
- ret = nbd_co_send_structured_error(req->client, request.handle,
- -ret, msg, &local_err);
- } else if (reply_data_len) {
- ret = nbd_co_send_structured_read(req->client, request.handle,
- request.from, req->data,
- reply_data_len, true,
- &local_err);
- } else {
- ret = nbd_co_send_structured_done(req->client, request.handle,
- &local_err);
- }
+ req = nbd_request_get(client);
+ ret = nbd_co_receive_request(req, &request, &local_err);
+ client->recv_coroutine = NULL;
+
+ if (client->closing) {
+ /*
+ * The client may be closed when we are blocked in
+ * nbd_co_receive_request()
+ */
+ goto done;
+ }
+
+ nbd_client_receive_next_request(client);
+ if (ret == -EIO) {
+ goto disconnect;
+ }
+
+ if (ret < 0) {
+ /* It wans't -EIO, so, according to nbd_co_receive_request()
+ * semantics, we should return the error to the client. */
+ Error *export_err = local_err;
+
+ local_err = NULL;
+ ret = nbd_send_generic_reply(client, request.handle, -EINVAL,
+ error_get_pretty(export_err), &local_err);
+ error_free(export_err);
} else {
- ret = nbd_co_send_simple_reply(req->client, request.handle,
- ret < 0 ? -ret : 0,
- req->data, reply_data_len, &local_err);
+ ret = nbd_handle_request(client, &request, req->data, &local_err);
}
- g_free(msg);
if (ret < 0) {
error_prepend(&local_err, "Failed to send reply: ");
goto disconnect;
diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin
index e1d6b15927..0061dc9928 100644
--- a/pc-bios/bios-256k.bin
+++ b/pc-bios/bios-256k.bin
Binary files differ
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
index 140e33c6b6..69bb8635ae 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
index 3c8d507c7a..cec498d59e 100644
--- a/pc-bios/vgabios-cirrus.bin
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin
index 1c9ec0f92d..82c9970df7 100644
--- a/pc-bios/vgabios-qxl.bin
+++ b/pc-bios/vgabios-qxl.bin
Binary files differ
diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin
index b84ae22de7..8029c2ae12 100644
--- a/pc-bios/vgabios-stdvga.bin
+++ b/pc-bios/vgabios-stdvga.bin
Binary files differ
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
index aebc7f820f..79333575e0 100644
--- a/pc-bios/vgabios-virtio.bin
+++ b/pc-bios/vgabios-virtio.bin
Binary files differ
diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin
index 76314aa323..ba1718c784 100644
--- a/pc-bios/vgabios-vmware.bin
+++ b/pc-bios/vgabios-vmware.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
index 066a40b4da..b624014bb9 100644
--- a/pc-bios/vgabios.bin
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 524d51567a..1088ab0c78 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -426,10 +426,13 @@
# @active: The bitmap is actively monitoring for new writes, and can be cleared,
# deleted, or used for backup operations.
#
+# @locked: The bitmap is currently in-use by some operation and can not be
+# cleared, deleted, or used for backup operations. (Since 2.12)
+#
# Since: 2.4
##
{ 'enum': 'DirtyBitmapStatus',
- 'data': ['active', 'disabled', 'frozen'] }
+ 'data': ['active', 'disabled', 'frozen', 'locked'] }
##
# @BlockDirtyInfo:
@@ -451,6 +454,106 @@
'status': 'DirtyBitmapStatus'} }
##
+# @BlockLatencyHistogramInfo:
+#
+# Block latency histogram.
+#
+# @boundaries: list of interval boundary values in nanoseconds, all greater
+# than zero and in ascending order.
+# For example, the list [10, 50, 100] produces the following
+# histogram intervals: [0, 10), [10, 50), [50, 100), [100, +inf).
+#
+# @bins: list of io request counts corresponding to histogram intervals.
+# len(@bins) = len(@boundaries) + 1
+# For the example above, @bins may be something like [3, 1, 5, 2],
+# and corresponding histogram looks like:
+#
+# 5| *
+# 4| *
+# 3| * *
+# 2| * * *
+# 1| * * * *
+# +------------------
+# 10 50 100
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockLatencyHistogramInfo',
+ 'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } }
+
+##
+# @x-block-latency-histogram-set:
+#
+# Manage read, write and flush latency histograms for the device.
+#
+# If only @device parameter is specified, remove all present latency histograms
+# for the device. Otherwise, add/reset some of (or all) latency histograms.
+#
+# @device: device name to set latency histogram for.
+#
+# @boundaries: list of interval boundary values (see description in
+# BlockLatencyHistogramInfo definition). If specified, all
+# latency histograms are removed, and empty ones created for all
+# io types with intervals corresponding to @boundaries (except for
+# io types, for which specific boundaries are set through the
+# following parameters).
+#
+# @boundaries-read: list of interval boundary values for read latency
+# histogram. If specified, old read latency histogram is
+# removed, and empty one created with intervals
+# corresponding to @boundaries-read. The parameter has higher
+# priority then @boundaries.
+#
+# @boundaries-write: list of interval boundary values for write latency
+# histogram.
+#
+# @boundaries-flush: list of interval boundary values for flush latency
+# histogram.
+#
+# Returns: error if device is not found or any boundary arrays are invalid.
+#
+# Since: 2.12
+#
+# Example: set new histograms for all io types with intervals
+# [0, 10), [10, 50), [50, 100), [100, +inf):
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries": [10, 50, 100] } }
+# <- { "return": {} }
+#
+# Example: set new histogram only for write, other histograms will remain
+# not changed (or not created):
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries-write": [10, 50, 100] } }
+# <- { "return": {} }
+#
+# Example: set new histograms with the following intervals:
+# read, flush: [0, 10), [10, 50), [50, 100), [100, +inf)
+# write: [0, 1000), [1000, 5000), [5000, +inf)
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0",
+# "boundaries": [10, 50, 100],
+# "boundaries-write": [1000, 5000] } }
+# <- { "return": {} }
+#
+# Example: remove all latency histograms:
+#
+# -> { "execute": "block-latency-histogram-set",
+# "arguments": { "device": "drive0" } }
+# <- { "return": {} }
+##
+{ 'command': 'x-block-latency-histogram-set',
+ 'data': {'device': 'str',
+ '*boundaries': ['uint64'],
+ '*boundaries-read': ['uint64'],
+ '*boundaries-write': ['uint64'],
+ '*boundaries-flush': ['uint64'] } }
+
+##
# @BlockInfo:
#
# Block device information. This structure describes a virtual device and
@@ -730,6 +833,12 @@
# @timed_stats: Statistics specific to the set of previously defined
# intervals of time (Since 2.5)
#
+# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
+# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
+# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12)
+#
# Since: 0.14.0
##
{ 'struct': 'BlockDeviceStats',
@@ -742,7 +851,10 @@
'failed_flush_operations': 'int', 'invalid_rd_operations': 'int',
'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int',
'account_invalid': 'bool', 'account_failed': 'bool',
- 'timed_stats': ['BlockDeviceTimedStats'] } }
+ 'timed_stats': ['BlockDeviceTimedStats'],
+ '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
+ '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
##
# @BlockStats:
@@ -956,6 +1068,77 @@
'data': ['commit', 'stream', 'mirror', 'backup'] }
##
+# @BlockJobVerb:
+#
+# Represents command verbs that can be applied to a blockjob.
+#
+# @cancel: see @block-job-cancel
+#
+# @pause: see @block-job-pause
+#
+# @resume: see @block-job-resume
+#
+# @set-speed: see @block-job-set-speed
+#
+# @complete: see @block-job-complete
+#
+# @dismiss: see @block-job-dismiss
+#
+# @finalize: see @block-job-finalize
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockJobVerb',
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
+ 'finalize' ] }
+
+##
+# @BlockJobStatus:
+#
+# Indicates the present state of a given blockjob in its lifetime.
+#
+# @undefined: Erroneous, default state. Should not ever be visible.
+#
+# @created: The job has been created, but not yet started.
+#
+# @running: The job is currently running.
+#
+# @paused: The job is running, but paused. The pause may be requested by
+# either the QMP user or by internal processes.
+#
+# @ready: The job is running, but is ready for the user to signal completion.
+# This is used for long-running jobs like mirror that are designed to
+# run indefinitely.
+#
+# @standby: The job is ready, but paused. This is nearly identical to @paused.
+# The job may return to @ready or otherwise be canceled.
+#
+# @waiting: The job is waiting for other jobs in the transaction to converge
+# to the waiting state. This status will likely not be visible for
+# the last job in a transaction.
+#
+# @pending: The job has finished its work, but has finalization steps that it
+# needs to make prior to completing. These changes may require
+# manual intervention by the management process if manual was set
+# to true. These changes may still fail.
+#
+# @aborting: The job is in the process of being aborted, and will finish with
+# an error. The job will afterwards report that it is @concluded.
+# This status may not be visible to the management process.
+#
+# @concluded: The job has finished all work. If manual was set to true, the job
+# will remain in the query list until it is dismissed.
+#
+# @null: The job is in the process of being dismantled. This state should not
+# ever be visible externally.
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockJobStatus',
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
+ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
+
+##
# @BlockJobInfo:
#
# Information about a long-running block device operation.
@@ -981,12 +1164,22 @@
#
# @ready: true if the job may be completed (since 2.2)
#
+# @status: Current job state/status (since 2.12)
+#
+# @auto-finalize: Job will finalize itself when PENDING, moving to
+# the CONCLUDED state. (since 2.12)
+#
+# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL
+# state and disappearing from the query list. (since 2.12)
+#
# Since: 1.1
##
{ 'struct': 'BlockJobInfo',
'data': {'type': 'str', 'device': 'str', 'len': 'int',
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
- 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
+ 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
+ 'status': 'BlockJobStatus',
+ 'auto-finalize': 'bool', 'auto-dismiss': 'bool' } }
##
# @query-block-jobs:
@@ -1090,7 +1283,7 @@
# @overlay: reference to the existing block device that will become
# the overlay of @node, as part of creating the snapshot.
# It must not have a current backing file (this can be
-# achieved by passing "backing": "" to blockdev-add).
+# achieved by passing "backing": null to blockdev-add).
#
# Since: 2.5
##
@@ -1136,6 +1329,18 @@
# default 'report' (no limitations, since this applies to
# a different block device than @device).
#
+# @auto-finalize: When false, this job will wait in a PENDING state after it has
+# finished its work, waiting for @block-job-finalize.
+# When true, this job will automatically perform its abort or
+# commit actions.
+# Defaults to true. (Since 2.12)
+#
+# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it
+# has completed ceased all work, and wait for @block-job-dismiss.
+# When true, this job will automatically disappear from the query
+# list without user intervention.
+# Defaults to true. (Since 2.12)
+#
# Note: @on-source-error and @on-target-error only affect background
# I/O. If an error occurs during a guest write request, the device's
# rerror/werror actions will be used.
@@ -1144,10 +1349,12 @@
##
{ 'struct': 'DriveBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
- '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
- '*speed': 'int', '*bitmap': 'str', '*compress': 'bool',
+ '*format': 'str', 'sync': 'MirrorSyncMode',
+ '*mode': 'NewImageMode', '*speed': 'int',
+ '*bitmap': 'str', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
- '*on-target-error': 'BlockdevOnError' } }
+ '*on-target-error': 'BlockdevOnError',
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
##
# @BlockdevBackup:
@@ -1177,6 +1384,18 @@
# default 'report' (no limitations, since this applies to
# a different block device than @device).
#
+# @auto-finalize: When false, this job will wait in a PENDING state after it has
+# finished its work, waiting for @block-job-finalize.
+# When true, this job will automatically perform its abort or
+# commit actions.
+# Defaults to true. (Since 2.12)
+#
+# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it
+# has completed ceased all work, and wait for @block-job-dismiss.
+# When true, this job will automatically disappear from the query
+# list without user intervention.
+# Defaults to true. (Since 2.12)
+#
# Note: @on-source-error and @on-target-error only affect background
# I/O. If an error occurs during a guest write request, the device's
# rerror/werror actions will be used.
@@ -1185,11 +1404,10 @@
##
{ 'struct': 'BlockdevBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
- 'sync': 'MirrorSyncMode',
- '*speed': 'int',
- '*compress': 'bool',
+ 'sync': 'MirrorSyncMode', '*speed': 'int', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
- '*on-target-error': 'BlockdevOnError' } }
+ '*on-target-error': 'BlockdevOnError',
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
##
# @blockdev-snapshot-sync:
@@ -1238,7 +1456,7 @@
# "node-name": "node1534",
# "file": { "driver": "file",
# "filename": "hd1.qcow2" },
-# "backing": "" } }
+# "backing": null } }
#
# <- { "return": {} }
#
@@ -2098,8 +2316,9 @@
# the name of the parameter), but since QEMU 2.7 it can have
# other values.
#
-# @force: whether to allow cancellation of a paused job (default
-# false). Since 1.3.
+# @force: If true, and the job has already emitted the event BLOCK_JOB_READY,
+# abandon the job immediately (even if it is paused) instead of waiting
+# for the destination to complete its final synchronization (since 1.3)
#
# Returns: Nothing on success
# If no background operation is active on this device, DeviceNotActive
@@ -2184,6 +2403,44 @@
{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
##
+# @block-job-dismiss:
+#
+# For jobs that have already concluded, remove them from the block-job-query
+# list. This command only needs to be run for jobs which were started with
+# QEMU 2.12+ job lifetime management semantics.
+#
+# This command will refuse to operate on any job that has not yet reached
+# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
+# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
+# to be used as appropriate.
+#
+# @id: The job identifier.
+#
+# Returns: Nothing on success
+#
+# Since: 2.12
+##
+{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } }
+
+##
+# @block-job-finalize:
+#
+# Once a job that has manual=true reaches the pending state, it can be
+# instructed to finalize any graph changes and do any necessary cleanup
+# via this command.
+# For jobs in a transaction, instructing one job to finalize will force
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
+# a single member job to finalize.
+#
+# @id: The job identifier.
+#
+# Returns: Nothing on success
+#
+# Since: 2.12
+##
+{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } }
+
+##
# @BlockdevDiscardOptions:
#
# Determines how to handle discard requests.
@@ -3452,6 +3709,21 @@
'*preallocation': 'PreallocMode' } }
##
+# @BlockdevCreateOptionsLUKS:
+#
+# Driver specific image creation options for LUKS.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsLUKS',
+ 'base': 'QCryptoBlockCreateOptionsLUKS',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size' } }
+
+##
# @BlockdevCreateOptionsNfs:
#
# Driver specific image creation options for NFS.
@@ -3466,6 +3738,41 @@
'size': 'size' } }
##
+# @BlockdevCreateOptionsParallels:
+#
+# Driver specific image creation options for parallels.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @cluster-size Cluster size in bytes (default: 1 MB)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsParallels',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*cluster-size': 'size' } }
+
+##
+# @BlockdevCreateOptionsQcow:
+#
+# Driver specific image creation options for qcow.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @backing-file File name of the backing file if a backing file
+# should be used
+# @encrypt Encryption options if the image should be encrypted
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsQcow',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*backing-file': 'str',
+ '*encrypt': 'QCryptoBlockCreateOptions' } }
+
+##
# @BlockdevQcow2Version:
#
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
@@ -3509,6 +3816,29 @@
'*refcount-bits': 'int' } }
##
+# @BlockdevCreateOptionsQed:
+#
+# Driver specific image creation options for qed.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @backing-file File name of the backing file if a backing file
+# should be used
+# @backing-fmt Name of the block driver to use for the backing file
+# @cluster-size Cluster size in bytes (default: 65536)
+# @table-size L1/L2 table size (in clusters)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsQed',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*backing-file': 'str',
+ '*backing-fmt': 'BlockdevDriver',
+ '*cluster-size': 'size',
+ '*table-size': 'int' } }
+
+##
# @BlockdevCreateOptionsRbd:
#
# Driver specific image creation options for rbd/Ceph.
@@ -3607,6 +3937,93 @@
'size': 'size' } }
##
+# @BlockdevCreateOptionsVdi:
+#
+# Driver specific image creation options for VDI.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @static Whether to create a statically (true) or
+# dynamically (false) allocated image
+# (default: false, i.e. dynamic)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsVdi',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*static': 'bool' } }
+
+##
+# @BlockdevVhdxSubformat:
+#
+# @dynamic: Growing image file
+# @fixed: Preallocated fixed-size image file
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockdevVhdxSubformat',
+ 'data': [ 'dynamic', 'fixed' ] }
+
+##
+# @BlockdevCreateOptionsVhdx:
+#
+# Driver specific image creation options for vhdx.
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @log-size Log size in bytes, must be a multiple of 1 MB
+# (default: 1 MB)
+# @block-size Block size in bytes, must be a multiple of 1 MB and not
+# larger than 256 MB (default: automatically choose a block
+# size depending on the image size)
+# @subformat vhdx subformat (default: dynamic)
+# @block-state-zero Force use of payload blocks of type 'ZERO'. Non-standard,
+# but default. Do not set to 'off' when using 'qemu-img
+# convert' with subformat=dynamic.
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsVhdx',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*log-size': 'size',
+ '*block-size': 'size',
+ '*subformat': 'BlockdevVhdxSubformat',
+ '*block-state-zero': 'bool' } }
+
+##
+# @BlockdevVpcSubformat:
+#
+# @dynamic: Growing image file
+# @fixed: Preallocated fixed-size image file
+#
+# Since: 2.12
+##
+{ 'enum': 'BlockdevVpcSubformat',
+ 'data': [ 'dynamic', 'fixed' ] }
+
+##
+# @BlockdevCreateOptionsVpc:
+#
+# Driver specific image creation options for vpc (VHD).
+#
+# @file Node to create the image format on
+# @size Size of the virtual disk in bytes
+# @subformat vhdx subformat (default: dynamic)
+# @force-size Force use of the exact byte size instead of rounding to the
+# next size that can be represented in CHS geometry
+# (default: false)
+#
+# Since: 2.12
+##
+{ 'struct': 'BlockdevCreateOptionsVpc',
+ 'data': { 'file': 'BlockdevRef',
+ 'size': 'size',
+ '*subformat': 'BlockdevVpcSubformat',
+ '*force-size': 'bool' } }
+
+##
# @BlockdevCreateNotSupported:
#
# This is used for all drivers that don't support creating images.
@@ -3643,16 +4060,16 @@
'http': 'BlockdevCreateNotSupported',
'https': 'BlockdevCreateNotSupported',
'iscsi': 'BlockdevCreateNotSupported',
- 'luks': 'BlockdevCreateNotSupported',
+ 'luks': 'BlockdevCreateOptionsLUKS',
'nbd': 'BlockdevCreateNotSupported',
'nfs': 'BlockdevCreateOptionsNfs',
'null-aio': 'BlockdevCreateNotSupported',
'null-co': 'BlockdevCreateNotSupported',
'nvme': 'BlockdevCreateNotSupported',
- 'parallels': 'BlockdevCreateNotSupported',
+ 'parallels': 'BlockdevCreateOptionsParallels',
+ 'qcow': 'BlockdevCreateOptionsQcow',
'qcow2': 'BlockdevCreateOptionsQcow2',
- 'qcow': 'BlockdevCreateNotSupported',
- 'qed': 'BlockdevCreateNotSupported',
+ 'qed': 'BlockdevCreateOptionsQed',
'quorum': 'BlockdevCreateNotSupported',
'raw': 'BlockdevCreateNotSupported',
'rbd': 'BlockdevCreateOptionsRbd',
@@ -3660,10 +4077,10 @@
'sheepdog': 'BlockdevCreateOptionsSheepdog',
'ssh': 'BlockdevCreateOptionsSsh',
'throttle': 'BlockdevCreateNotSupported',
- 'vdi': 'BlockdevCreateNotSupported',
- 'vhdx': 'BlockdevCreateNotSupported',
+ 'vdi': 'BlockdevCreateOptionsVdi',
+ 'vhdx': 'BlockdevCreateOptionsVhdx',
'vmdk': 'BlockdevCreateNotSupported',
- 'vpc': 'BlockdevCreateNotSupported',
+ 'vpc': 'BlockdevCreateOptionsVpc',
'vvfat': 'BlockdevCreateNotSupported',
'vxhs': 'BlockdevCreateNotSupported'
} }
@@ -4177,6 +4594,30 @@
'speed' : 'int' } }
##
+# @BLOCK_JOB_PENDING:
+#
+# Emitted when a block job is awaiting explicit authorization to finalize graph
+# changes via @block-job-finalize. If this job is part of a transaction, it will
+# not emit this event until the transaction has converged first.
+#
+# @type: job type
+#
+# @id: The job identifier.
+#
+# Since: 2.12
+#
+# Example:
+#
+# <- { "event": "BLOCK_JOB_WAITING",
+# "data": { "device": "drive0", "type": "mirror" },
+# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+#
+##
+{ 'event': 'BLOCK_JOB_PENDING',
+ 'data': { 'type' : 'BlockJobType',
+ 'id' : 'str' } }
+
+##
# @PreallocMode:
#
# Preallocation mode of QEMU image file
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 5b3e6e9d78..c7f67b7d78 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -259,12 +259,16 @@
#
# @ret-type: the name of the command's result type.
#
+# @allow-oob: whether the command allows out-of-band execution.
+# (Since: 2.12)
+#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
- 'data': { 'arg-type': 'str', 'ret-type': 'str' } }
+ 'data': { 'arg-type': 'str', 'ret-type': 'str',
+ 'allow-oob': 'bool' } }
##
# @SchemaInfoEvent:
diff --git a/qapi/migration.json b/qapi/migration.json
index 7f465a1902..9d0bf82cf4 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -354,12 +354,16 @@
#
# @x-multifd: Use more than one fd for migration (since 2.11)
#
+# @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps.
+# (since 2.12)
+#
# Since: 1.2
##
{ 'enum': 'MigrationCapability',
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram',
- 'block', 'return-path', 'pause-before-switchover', 'x-multifd' ] }
+ 'block', 'return-path', 'pause-before-switchover', 'x-multifd',
+ 'dirty-bitmaps' ] }
##
# @MigrationCapabilityStatus:
diff --git a/qapi/misc.json b/qapi/misc.json
index 6bf082f612..5636f4a149 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -10,21 +10,47 @@
#
# Enable QMP capabilities.
#
-# Arguments: None.
+# Arguments:
+#
+# @enable: An optional list of QMPCapability values to enable. The
+# client must not enable any capability that is not
+# mentioned in the QMP greeting message. If the field is not
+# provided, it means no QMP capabilities will be enabled.
+# (since 2.12)
#
# Example:
#
-# -> { "execute": "qmp_capabilities" }
+# -> { "execute": "qmp_capabilities",
+# "arguments": { "enable": [ "oob" ] } }
# <- { "return": {} }
#
# Notes: This command is valid exactly when first connecting: it must be
# issued before any other command will be accepted, and will fail once the
# monitor is accepting other commands. (see qemu docs/interop/qmp-spec.txt)
#
+# The QMP client needs to explicitly enable QMP capabilities, otherwise
+# all the QMP capabilities will be turned off by default.
+#
# Since: 0.13
#
##
-{ 'command': 'qmp_capabilities' }
+{ 'command': 'qmp_capabilities',
+ 'data': { '*enable': [ 'QMPCapability' ] } }
+
+##
+# @QMPCapability:
+#
+# Enumeration of capabilities to be advertised during initial client
+# connection, used for agreeing on particular QMP extension behaviors.
+#
+# @oob: QMP ability to support Out-Of-Band requests.
+# (Please refer to qmp-spec.txt for more information on OOB)
+#
+# Since: 2.12
+#
+##
+{ 'enum': 'QMPCapability',
+ 'data': [ 'oob' ] }
##
# @VersionTriple:
@@ -3220,3 +3246,206 @@
# Since: 2.9
##
{ 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' }
+
+
+##
+# @SevState:
+#
+# An enumeration of SEV state information used during @query-sev.
+#
+# @uninit: The guest is uninitialized.
+#
+# @launch-update: The guest is currently being launched; plaintext data and
+# register state is being imported.
+#
+# @launch-secret: The guest is currently being launched; ciphertext data
+# is being imported.
+#
+# @running: The guest is fully launched or migrated in.
+#
+# @send-update: The guest is currently being migrated out to another machine.
+#
+# @receive-update: The guest is currently being migrated from another machine.
+#
+# Since: 2.12
+##
+{ 'enum': 'SevState',
+ 'data': ['uninit', 'launch-update', 'launch-secret', 'running',
+ 'send-update', 'receive-update' ] }
+
+##
+# @SevInfo:
+#
+# Information about Secure Encrypted Virtualization (SEV) support
+#
+# @enabled: true if SEV is active
+#
+# @api-major: SEV API major version
+#
+# @api-minor: SEV API minor version
+#
+# @build-id: SEV FW build id
+#
+# @policy: SEV policy value
+#
+# @state: SEV guest state
+#
+# @handle: SEV firmware handle
+#
+# Since: 2.12
+##
+{ 'struct': 'SevInfo',
+ 'data': { 'enabled': 'bool',
+ 'api-major': 'uint8',
+ 'api-minor' : 'uint8',
+ 'build-id' : 'uint8',
+ 'policy' : 'uint32',
+ 'state' : 'SevState',
+ 'handle' : 'uint32'
+ }
+}
+
+##
+# @query-sev:
+#
+# Returns information about SEV
+#
+# Returns: @SevInfo
+#
+# Since: 2.12
+#
+# Example:
+#
+# -> { "execute": "query-sev" }
+# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0,
+# "build-id" : 0, "policy" : 0, "state" : "running",
+# "handle" : 1 } }
+#
+##
+{ 'command': 'query-sev', 'returns': 'SevInfo' }
+
+##
+# @SevLaunchMeasureInfo:
+#
+# SEV Guest Launch measurement information
+#
+# @data: the measurement value encoded in base64
+#
+# Since: 2.12
+#
+##
+{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} }
+
+##
+# @query-sev-launch-measure:
+#
+# Query the SEV guest launch information.
+#
+# Returns: The @SevLaunchMeasureInfo for the guest
+#
+# Since: 2.12
+#
+# Example:
+#
+# -> { "execute": "query-sev-launch-measure" }
+# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } }
+#
+##
+{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' }
+
+##
+# @SevCapability:
+#
+# The struct describes capability for a Secure Encrypted Virtualization
+# feature.
+#
+# @pdh: Platform Diffie-Hellman key (base64 encoded)
+#
+# @cert-chain: PDH certificate chain (base64 encoded)
+#
+# @cbitpos: C-bit location in page table entry
+#
+# @reduced-phys-bits: Number of physical Address bit reduction when SEV is
+# enabled
+#
+# Since: 2.12
+##
+{ 'struct': 'SevCapability',
+ 'data': { 'pdh': 'str',
+ 'cert-chain': 'str',
+ 'cbitpos': 'int',
+ 'reduced-phys-bits': 'int'} }
+
+##
+# @query-sev-capabilities:
+#
+# This command is used to get the SEV capabilities, and is supported on AMD
+# X86 platforms only.
+#
+# Returns: SevCapability objects.
+#
+# Since: 2.12
+#
+# Example:
+#
+# -> { "execute": "query-sev-capabilities" }
+# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE",
+# "cbitpos": 47, "reduced-phys-bits": 5}}
+#
+##
+{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' }
+
+##
+# @CommandDropReason:
+#
+# Reasons that caused one command to be dropped.
+#
+# @queue-full: the command queue is full. This can only occur when
+# the client sends a new non-oob command before the
+# response to the previous non-oob command has been
+# received.
+#
+# Since: 2.12
+##
+{ 'enum': 'CommandDropReason',
+ 'data': [ 'queue-full' ] }
+
+##
+# @COMMAND_DROPPED:
+#
+# Emitted when a command is dropped due to some reason. Commands can
+# only be dropped when the oob capability is enabled.
+#
+# @id: The dropped command's "id" field.
+#
+# @reason: The reason why the command is dropped.
+#
+# Since: 2.12
+#
+# Example:
+#
+# { "event": "COMMAND_DROPPED",
+# "data": {"result": {"id": "libvirt-102",
+# "reason": "queue-full" } } }
+#
+##
+{ 'event': 'COMMAND_DROPPED' ,
+ 'data': { 'id': 'any', 'reason': 'CommandDropReason' } }
+
+##
+# @x-oob-test:
+#
+# Test OOB functionality. When sending this command with lock=true,
+# it'll try to hang the dispatcher. When sending it with lock=false,
+# it'll try to notify the locked thread to continue. Note: it should
+# only be used by QMP test program rather than anything else.
+#
+# Since: 2.12
+#
+# Example:
+#
+# { "execute": "x-oob-test",
+# "arguments": { "lock": true } }
+##
+{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
+ 'allow-oob': true }
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index e31ac4be1f..dd05907265 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -17,8 +17,9 @@
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qbool.h"
-static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
+QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
{
const QDictEntry *ent;
const char *arg_name;
@@ -26,7 +27,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
bool has_exec_key = false;
QDict *dict = NULL;
- dict = qobject_to_qdict(request);
+ dict = qobject_to(QDict, request);
if (!dict) {
error_setg(errp, "QMP input must be a JSON object");
return NULL;
@@ -50,6 +51,14 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
"QMP input member 'arguments' must be an object");
return NULL;
}
+ } else if (!strcmp(arg_name, "id")) {
+ continue;
+ } else if (!strcmp(arg_name, "control")) {
+ if (qobject_type(arg_obj) != QTYPE_QDICT) {
+ error_setg(errp,
+ "QMP input member 'control' must be a dict");
+ return NULL;
+ }
} else {
error_setg(errp, "QMP input member '%s' is unexpected",
arg_name);
@@ -120,6 +129,28 @@ QObject *qmp_build_error_object(Error *err)
error_get_pretty(err));
}
+/*
+ * Detect whether a request should be run out-of-band, by quickly
+ * peeking at whether we have: { "control": { "run-oob": true } }. By
+ * default commands are run in-band.
+ */
+bool qmp_is_oob(QDict *dict)
+{
+ QBool *bool_obj;
+
+ dict = qdict_get_qdict(dict, "control");
+ if (!dict) {
+ return false;
+ }
+
+ bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob"));
+ if (!bool_obj) {
+ return false;
+ }
+
+ return qbool_get_bool(bool_obj);
+}
+
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
{
Error *err = NULL;
diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c
index 023317b05f..a7569d5dce 100644
--- a/qapi/qobject-input-visitor.c
+++ b/qapi/qobject-input-visitor.c
@@ -137,7 +137,7 @@ static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
if (qobject_type(qobj) == QTYPE_QDICT) {
assert(name);
- ret = qdict_get(qobject_to_qdict(qobj), name);
+ ret = qdict_get(qobject_to(QDict, qobj), name);
if (tos->h && consume && ret) {
bool removed = g_hash_table_remove(tos->h, name);
assert(removed);
@@ -185,7 +185,7 @@ static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv,
return NULL;
}
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
if (!qstr) {
switch (qobject_type(qobj)) {
case QTYPE_QDICT:
@@ -224,11 +224,11 @@ static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
if (qobject_type(obj) == QTYPE_QDICT) {
h = g_hash_table_new(g_str_hash, g_str_equal);
- qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
+ qdict_iter(qobject_to(QDict, obj), qdict_add_key, h);
tos->h = h;
} else {
assert(qobject_type(obj) == QTYPE_QLIST);
- tos->entry = qlist_first(qobject_to_qlist(obj));
+ tos->entry = qlist_first(qobject_to(QList, obj));
tos->index = -1;
}
@@ -339,7 +339,7 @@ static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
QObjectInputVisitor *qiv = to_qiv(v);
StackObject *tos = QSLIST_FIRST(&qiv->stack);
- assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
+ assert(tos && qobject_to(QList, tos->obj));
if (!tos->entry) {
return NULL;
@@ -353,7 +353,7 @@ static void qobject_input_check_list(Visitor *v, Error **errp)
QObjectInputVisitor *qiv = to_qiv(v);
StackObject *tos = QSLIST_FIRST(&qiv->stack);
- assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
+ assert(tos && qobject_to(QList, tos->obj));
if (tos->entry) {
error_setg(errp, "Only %u list elements expected in %s",
@@ -395,7 +395,7 @@ static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum || !qnum_get_try_int(qnum, obj)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "integer");
@@ -430,7 +430,7 @@ static void qobject_input_type_uint64(Visitor *v, const char *name,
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum) {
goto err;
}
@@ -477,7 +477,7 @@ static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
if (!qobj) {
return;
}
- qbool = qobject_to_qbool(qobj);
+ qbool = qobject_to(QBool, qobj);
if (!qbool) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "boolean");
@@ -518,7 +518,7 @@ static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
if (!qobj) {
return;
}
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
if (!qstr) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "string");
@@ -547,7 +547,7 @@ static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
if (!qobj) {
return;
}
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
if (!qnum) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "number");
@@ -734,7 +734,7 @@ Visitor *qobject_input_visitor_new_str(const char *str,
}
return NULL;
}
- args = qobject_to_qdict(obj);
+ args = qobject_to(QDict, obj);
assert(args);
v = qobject_input_visitor_new(QOBJECT(args));
} else {
diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c
index 7c3b42cfe2..877e37eeb8 100644
--- a/qapi/qobject-output-visitor.c
+++ b/qapi/qobject-output-visitor.c
@@ -92,11 +92,11 @@ static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name,
switch (qobject_type(cur)) {
case QTYPE_QDICT:
assert(name);
- qdict_put_obj(qobject_to_qdict(cur), name, value);
+ qdict_put_obj(qobject_to(QDict, cur), name, value);
break;
case QTYPE_QLIST:
assert(!name);
- qlist_append_obj(qobject_to_qlist(cur), value);
+ qlist_append_obj(qobject_to(QList, cur), value);
break;
default:
g_assert_not_reached();
diff --git a/qapi/sockets.json b/qapi/sockets.json
index ac022c6ad0..fc81d8d5e8 100644
--- a/qapi/sockets.json
+++ b/qapi/sockets.json
@@ -123,6 +123,13 @@
#
# @unix: Unix domain socket
#
+# @vsock: VMCI address
+#
+# @fd: decimal is for file descriptor number, otherwise a file descriptor name.
+# Named file descriptors are permitted in monitor commands, in combination
+# with the 'getfd' command. Decimal file descriptors are permitted at
+# startup or other contexts where no monitor context is active.
+#
# Since: 2.9
##
{ 'enum': 'SocketAddressType',
diff --git a/qdev-monitor.c b/qdev-monitor.c
index b7e3291f8b..61e0300991 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -122,12 +122,6 @@ static void qdev_print_devinfo(DeviceClass *dc)
error_printf("\n");
}
-static gint devinfo_cmp(gconstpointer a, gconstpointer b)
-{
- return strcasecmp(object_class_get_name((ObjectClass *)a),
- object_class_get_name((ObjectClass *)b));
-}
-
static void qdev_print_devinfos(bool show_no_user)
{
static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
@@ -146,8 +140,7 @@ static void qdev_print_devinfos(bool show_no_user)
int i;
bool cat_printed;
- list = g_slist_sort(object_class_get_list(TYPE_DEVICE, false),
- devinfo_cmp);
+ list = object_class_get_list_sorted(TYPE_DEVICE, false);
for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
cat_printed = false;
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 39e38c87ec..89fa80518a 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2643,42 +2643,46 @@ combined with ``-vnc tls-creds=tls0'
@subsection -tftp (since 2.6.0)
-The ``-tftp /some/dir'' argument is replaced by
-``-netdev user,id=x,tftp=/some/dir'', either accompanied with
-``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+The ``-tftp /some/dir'' argument is replaced by either
+``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied
+with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -bootp (since 2.6.0)
-The ``-bootp /some/file'' argument is replaced by
-``-netdev user,id=x,bootp=/some/file'', either accompanied with
-``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+The ``-bootp /some/file'' argument is replaced by either
+``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied
+with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -redir (since 2.6.0)
The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is
-replaced by ``-netdev
-user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'',
-either accompanied with ``-device ...,netdev=x'' (for pluggable NICs) or
-``-net nic,netdev=x'' (for embedded NICs). The new syntax allows different
-settings to be provided per NIC.
+replaced by either
+``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport''
+(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or
+``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport''
+(for embedded NICs). The new syntax allows different settings to be
+provided per NIC.
@subsection -smb (since 2.6.0)
-The ``-smb /some/dir'' argument is replaced by
-``-netdev user,id=x,smb=/some/dir'', either accompanied with
-``-device ...,netdev=x'' (for pluggable NICs) or ``-net nic,netdev=x''
+The ``-smb /some/dir'' argument is replaced by either
+``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied
+with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir''
(for embedded NICs). The new syntax allows different settings to be
provided per NIC.
@subsection -net vlan (since 2.9.0)
-The ``-net vlan=NN'' argument is partially replaced with the
-new ``-netdev'' argument. The remaining use cases will no
-longer be directly supported in QEMU.
+The ``-net vlan=NN'' argument was mostly used to attach separate
+network backends to different virtual NICs. This is the default
+behavior for ``-netdev'' and ``-nic''. You can connect multiple
+``-netdev'' and ``-nic'' devices to the same network using the
+"hubport" network backend, created with ``-netdev hubport,hubid=NN,...''
+and ``-nic hubport,hubid=NN''.
@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
@@ -2728,6 +2732,12 @@ filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability,
which is not the recommended way to run QEMU. This backend should not be
used and it will be removed with no replacement.
+@subsection -no-frame (since 2.12.0)
+
+The @code{--no-frame} argument works with SDL 1.2 only. The other user
+interfaces never implemented this in the first place. So this will be
+removed together with SDL 1.2 support.
+
@subsection -rtc-td-hack (since 2.12.0)
The @code{-rtc-td-hack} option has been replaced by
@@ -2779,6 +2789,13 @@ support page sizes < 4096 any longer.
The ``xlnx-ep108'' machine has been replaced by the ``xlnx-zcu102'' machine.
The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU.
+@section Block device options
+
+@subsection "backing": "" (since 2.12.0)
+
+In order to prevent QEMU from automatically opening an image's backing
+chain, use ``"backing": null'' instead.
+
@node License
@appendix License
diff --git a/qemu-img.c b/qemu-img.c
index 088d89043e..855fa52514 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -46,7 +46,7 @@
#include "crypto/init.h"
#include "trace/control.h"
-#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \
+#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
"\n" QEMU_COPYRIGHT "\n"
typedef struct img_cmd_t {
diff --git a/qemu-io.c b/qemu-io.c
index 160fb2a89f..e692c555e0 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -558,7 +558,7 @@ int main(int argc, char **argv)
trace_file = trace_opt_parse(optarg);
break;
case 'V':
- printf("%s version " QEMU_VERSION QEMU_PKGVERSION "\n"
+ printf("%s version " QEMU_FULL_VERSION "\n"
QEMU_COPYRIGHT "\n", progname);
exit(0);
case 'h':
diff --git a/qemu-nbd.c b/qemu-nbd.c
index ed5d9b5062..0af0560ad1 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -130,7 +130,7 @@ QEMU_HELP_BOTTOM "\n"
static void version(const char *name)
{
printf(
-"%s " QEMU_VERSION QEMU_PKGVERSION "\n"
+"%s " QEMU_FULL_VERSION "\n"
"Written by Anthony Liguori.\n"
"\n"
QEMU_COPYRIGHT "\n"
diff --git a/qemu-options.hx b/qemu-options.hx
index 6585058c6c..74158e7493 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -43,7 +43,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n"
" nvdimm=on|off controls NVDIMM support (default=off)\n"
" enforce-config-section=on|off enforce configuration section migration (default=off)\n"
- " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n",
+ " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n"
+ " memory-encryption=@var{} memory encryption object to use (default=none)\n",
QEMU_ARCH_ALL)
STEXI
@item -machine [type=]@var{name}[,prop=@var{value}[,...]]
@@ -110,6 +111,8 @@ code to send configuration section even if the machine-type sets the
@option{migration.send-configuration} property to @var{off}.
NOTE: this parameter is deprecated. Please use @option{-global}
@option{migration.send-configuration}=@var{on|off} instead.
+@item memory-encryption=@var{}
+Memory encryption object to use. The default is none.
@end table
ETEXI
@@ -740,8 +743,8 @@ Reference to or definition of the data source block driver node
@item backing
Reference to or definition of the backing file block device (default is taken
-from the image file). It is allowed to pass an empty string here in order to
-disable the default backing file.
+from the image file). It is allowed to pass @code{null} here in order to disable
+the default backing file.
@item lazy-refcounts
Whether to enable the lazy refcounts feature (on/off; default is taken from the
@@ -4350,6 +4353,50 @@ contents of @code{iv.b64} to the second secret
data=$SECRET,iv=$(<iv.b64)
@end example
+@item -object sev-guest,id=@var{id},cbitpos=@var{cbitpos},reduced-phys-bits=@var{val},[sev-device=@var{string},policy=@var{policy},handle=@var{handle},dh-cert-file=@var{file},session-file=@var{file}]
+
+Create a Secure Encrypted Virtualization (SEV) guest object, which can be used
+to provide the guest memory encryption support on AMD processors.
+
+When memory encryption is enabled, one of the physical address bit (aka the
+C-bit) is utilized to mark if a memory page is protected. The @option{cbitpos}
+is used to provide the C-bit position. The C-bit position is Host family dependent
+hence user must provide this value. On EPYC, the value should be 47.
+
+When memory encryption is enabled, we loose certain bits in physical address space.
+The @option{reduced-phys-bits} is used to provide the number of bits we loose in
+physical address space. Similar to C-bit, the value is Host family dependent.
+On EPYC, the value should be 5.
+
+The @option{sev-device} provides the device file to use for communicating with
+the SEV firmware running inside AMD Secure Processor. The default device is
+'/dev/sev'. If hardware supports memory encryption then /dev/sev devices are
+created by CCP driver.
+
+The @option{policy} provides the guest policy to be enforced by the SEV firmware
+and restrict what configuration and operational commands can be performed on this
+guest by the hypervisor. The policy should be provided by the guest owner and is
+bound to the guest and cannot be changed throughout the lifetime of the guest.
+The default is 0.
+
+If guest @option{policy} allows sharing the key with another SEV guest then
+@option{handle} can be use to provide handle of the guest from which to share
+the key.
+
+The @option{dh-cert-file} and @option{session-file} provides the guest owner's
+Public Diffie-Hillman key defined in SEV spec. The PDH and session parameters
+are used for establishing a cryptographic session with the guest owner to
+negotiate keys used for attestation. The file must be encoded in base64.
+
+e.g to launch a SEV guest
+@example
+ # $QEMU \
+ ......
+ -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=5 \
+ -machine ...,memory-encryption=sev0
+ .....
+
+@end example
@end table
ETEXI
diff --git a/qga/main.c b/qga/main.c
index f9c83050c5..df1888edc1 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -218,7 +218,7 @@ static void usage(const char *cmd)
{
printf(
"Usage: %s [-m <method> -p <path>] [<options>]\n"
-"QEMU Guest Agent " QEMU_VERSION QEMU_PKGVERSION "\n"
+"QEMU Guest Agent " QEMU_FULL_VERSION "\n"
QEMU_COPYRIGHT "\n"
"\n"
" -m, --method transport method: one of unix-listen, virtio-serial,\n"
@@ -607,7 +607,7 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
g_assert(s && parser);
g_debug("process_event: called");
- qdict = qobject_to_qdict(json_parser_parse_err(tokens, NULL, &err));
+ qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
if (err || !qdict) {
QDECREF(qdict);
qdict = qdict_new();
diff --git a/qmp.c b/qmp.c
index 4b2517d8b5..f72261667f 100644
--- a/qmp.c
+++ b/qmp.c
@@ -705,7 +705,7 @@ void qmp_object_add(const char *type, const char *id,
Object *obj;
if (props) {
- pdict = qobject_to_qdict(props);
+ pdict = qobject_to(QDict, props);
if (!pdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
return;
@@ -765,3 +765,19 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp)
return mem_info;
}
+
+static QemuSemaphore x_oob_test_sem;
+
+static void __attribute__((constructor)) x_oob_test_init(void)
+{
+ qemu_sem_init(&x_oob_test_sem, 0);
+}
+
+void qmp_x_oob_test(bool lock, Error **errp)
+{
+ if (lock) {
+ qemu_sem_wait(&x_oob_test_sem);
+ } else {
+ qemu_sem_post(&x_oob_test_sem);
+ }
+}
diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index b724562415..769b960c9f 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -276,7 +276,8 @@ static void parser_context_free(JSONParserContext *ctxt)
*/
static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
{
- QObject *key = NULL, *value;
+ QObject *value;
+ QString *key = NULL;
JSONToken *peek, *token;
peek = parser_context_peek_token(ctxt);
@@ -285,8 +286,8 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
goto out;
}
- key = parse_value(ctxt, ap);
- if (!key || qobject_type(key) != QTYPE_QSTRING) {
+ key = qobject_to(QString, parse_value(ctxt, ap));
+ if (!key) {
parse_error(ctxt, peek, "key is not a string in object");
goto out;
}
@@ -308,14 +309,14 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
goto out;
}
- qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value);
+ qdict_put_obj(dict, qstring_get_str(key), value);
- qobject_decref(key);
+ QDECREF(key);
return 0;
out:
- qobject_decref(key);
+ QDECREF(key);
return -1;
}
diff --git a/qobject/qbool.c b/qobject/qbool.c
index e5a7a53879..b58249925c 100644
--- a/qobject/qbool.c
+++ b/qobject/qbool.c
@@ -40,22 +40,11 @@ bool qbool_get_bool(const QBool *qb)
}
/**
- * qobject_to_qbool(): Convert a QObject into a QBool
- */
-QBool *qobject_to_qbool(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QBOOL) {
- return NULL;
- }
- return container_of(obj, QBool, base);
-}
-
-/**
* qbool_is_equal(): Test whether the two QBools are equal
*/
bool qbool_is_equal(const QObject *x, const QObject *y)
{
- return qobject_to_qbool(x)->value == qobject_to_qbool(y)->value;
+ return qobject_to(QBool, x)->value == qobject_to(QBool, y)->value;
}
/**
@@ -65,5 +54,5 @@ bool qbool_is_equal(const QObject *x, const QObject *y)
void qbool_destroy_obj(QObject *obj)
{
assert(obj != NULL);
- g_free(qobject_to_qbool(obj));
+ g_free(qobject_to(QBool, obj));
}
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 229b8c840b..d1997a0d8a 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -38,17 +38,6 @@ QDict *qdict_new(void)
}
/**
- * qobject_to_qdict(): Convert a QObject into a QDict
- */
-QDict *qobject_to_qdict(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QDICT) {
- return NULL;
- }
- return container_of(obj, QDict, base);
-}
-
-/**
* tdb_hash(): based on the hash agorithm from gdbm, via tdb
* (from module-init-tools)
*/
@@ -206,7 +195,7 @@ size_t qdict_size(const QDict *qdict)
*/
double qdict_get_double(const QDict *qdict, const char *key)
{
- return qnum_get_double(qobject_to_qnum(qdict_get(qdict, key)));
+ return qnum_get_double(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
@@ -219,7 +208,7 @@ double qdict_get_double(const QDict *qdict, const char *key)
*/
int64_t qdict_get_int(const QDict *qdict, const char *key)
{
- return qnum_get_int(qobject_to_qnum(qdict_get(qdict, key)));
+ return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key)));
}
/**
@@ -232,7 +221,7 @@ int64_t qdict_get_int(const QDict *qdict, const char *key)
*/
bool qdict_get_bool(const QDict *qdict, const char *key)
{
- return qbool_get_bool(qobject_to_qbool(qdict_get(qdict, key)));
+ return qbool_get_bool(qobject_to(QBool, qdict_get(qdict, key)));
}
/**
@@ -240,7 +229,7 @@ bool qdict_get_bool(const QDict *qdict, const char *key)
*/
QList *qdict_get_qlist(const QDict *qdict, const char *key)
{
- return qobject_to_qlist(qdict_get(qdict, key));
+ return qobject_to(QList, qdict_get(qdict, key));
}
/**
@@ -248,7 +237,7 @@ QList *qdict_get_qlist(const QDict *qdict, const char *key)
*/
QDict *qdict_get_qdict(const QDict *qdict, const char *key)
{
- return qobject_to_qdict(qdict_get(qdict, key));
+ return qobject_to(QDict, qdict_get(qdict, key));
}
/**
@@ -262,7 +251,7 @@ QDict *qdict_get_qdict(const QDict *qdict, const char *key)
*/
const char *qdict_get_str(const QDict *qdict, const char *key)
{
- return qstring_get_str(qobject_to_qstring(qdict_get(qdict, key)));
+ return qstring_get_str(qobject_to(QString, qdict_get(qdict, key)));
}
/**
@@ -275,7 +264,7 @@ const char *qdict_get_str(const QDict *qdict, const char *key)
int64_t qdict_get_try_int(const QDict *qdict, const char *key,
int64_t def_value)
{
- QNum *qnum = qobject_to_qnum(qdict_get(qdict, key));
+ QNum *qnum = qobject_to(QNum, qdict_get(qdict, key));
int64_t val;
if (!qnum || !qnum_get_try_int(qnum, &val)) {
@@ -294,7 +283,7 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key,
*/
bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value)
{
- QBool *qbool = qobject_to_qbool(qdict_get(qdict, key));
+ QBool *qbool = qobject_to(QBool, qdict_get(qdict, key));
return qbool ? qbool_get_bool(qbool) : def_value;
}
@@ -309,7 +298,7 @@ bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value)
*/
const char *qdict_get_try_str(const QDict *qdict, const char *key)
{
- QString *qstr = qobject_to_qstring(qdict_get(qdict, key));
+ QString *qstr = qobject_to(QString, qdict_get(qdict, key));
return qstr ? qstring_get_str(qstr) : NULL;
}
@@ -432,8 +421,8 @@ void qdict_del(QDict *qdict, const char *key)
*/
bool qdict_is_equal(const QObject *x, const QObject *y)
{
- const QDict *dict_x = qobject_to_qdict(x);
- const QDict *dict_y = qobject_to_qdict(y);
+ const QDict *dict_x = qobject_to(QDict, x);
+ const QDict *dict_y = qobject_to(QDict, y);
const QDictEntry *e;
if (qdict_size(dict_x) != qdict_size(dict_y)) {
@@ -461,7 +450,7 @@ void qdict_destroy_obj(QObject *obj)
QDict *qdict;
assert(obj != NULL);
- qdict = qobject_to_qdict(obj);
+ qdict = qobject_to(QDict, obj);
for (i = 0; i < QDICT_BUCKET_MAX; i++) {
QDictEntry *entry = QLIST_FIRST(&qdict->table[i]);
@@ -532,9 +521,9 @@ static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix)
new_key = g_strdup_printf("%s.%i", prefix, i);
if (qobject_type(value) == QTYPE_QDICT) {
- qdict_flatten_qdict(qobject_to_qdict(value), target, new_key);
+ qdict_flatten_qdict(qobject_to(QDict, value), target, new_key);
} else if (qobject_type(value) == QTYPE_QLIST) {
- qdict_flatten_qlist(qobject_to_qlist(value), target, new_key);
+ qdict_flatten_qlist(qobject_to(QList, value), target, new_key);
} else {
/* All other types are moved to the target unchanged. */
qobject_incref(value);
@@ -568,11 +557,11 @@ static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix)
if (qobject_type(value) == QTYPE_QDICT) {
/* Entries of QDicts are processed recursively, the QDict object
* itself disappears. */
- qdict_flatten_qdict(qobject_to_qdict(value), target,
+ qdict_flatten_qdict(qobject_to(QDict, value), target,
new_key ? new_key : entry->key);
delete = true;
} else if (qobject_type(value) == QTYPE_QLIST) {
- qdict_flatten_qlist(qobject_to_qlist(value), target,
+ qdict_flatten_qlist(qobject_to(QList, value), target,
new_key ? new_key : entry->key);
delete = true;
} else if (prefix) {
@@ -893,18 +882,20 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
child = qdict_get(two_level, prefix);
if (suffix) {
- if (child) {
- if (qobject_type(child) != QTYPE_QDICT) {
+ QDict *child_dict = qobject_to(QDict, child);
+ if (!child_dict) {
+ if (child) {
error_setg(errp, "Key %s prefix is already set as a scalar",
prefix);
goto error;
}
- } else {
- child = QOBJECT(qdict_new());
- qdict_put_obj(two_level, prefix, child);
+
+ child_dict = qdict_new();
+ qdict_put_obj(two_level, prefix, QOBJECT(child_dict));
}
+
qobject_incref(ent->value);
- qdict_put_obj(qobject_to_qdict(child), suffix, ent->value);
+ qdict_put_obj(child_dict, suffix, ent->value);
} else {
if (child) {
error_setg(errp, "Key %s prefix is already set as a dict",
@@ -924,9 +915,9 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
multi_level = qdict_new();
for (ent = qdict_first(two_level); ent != NULL;
ent = qdict_next(two_level, ent)) {
-
- if (qobject_type(ent->value) == QTYPE_QDICT) {
- child = qdict_crumple(qobject_to_qdict(ent->value), errp);
+ QDict *dict = qobject_to(QDict, ent->value);
+ if (dict) {
+ child = qdict_crumple(dict, errp);
if (!child) {
goto error;
}
@@ -961,7 +952,7 @@ QObject *qdict_crumple(const QDict *src, Error **errp)
}
qobject_incref(child);
- qlist_append_obj(qobject_to_qlist(dst), child);
+ qlist_append_obj(qobject_to(QList, dst), child);
}
QDECREF(multi_level);
multi_level = NULL;
diff --git a/qobject/qjson.c b/qobject/qjson.c
index e1ce75651c..655d38adf1 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -137,14 +137,14 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
qstring_append(str, "null");
break;
case QTYPE_QNUM: {
- QNum *val = qobject_to_qnum(obj);
+ QNum *val = qobject_to(QNum, obj);
char *buffer = qnum_to_string(val);
qstring_append(str, buffer);
g_free(buffer);
break;
}
case QTYPE_QSTRING: {
- QString *val = qobject_to_qstring(obj);
+ QString *val = qobject_to(QString, obj);
const char *ptr;
int cp;
char buf[16];
@@ -201,7 +201,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
}
case QTYPE_QDICT: {
ToJsonIterState s;
- QDict *val = qobject_to_qdict(obj);
+ QDict *val = qobject_to(QDict, obj);
s.count = 0;
s.str = str;
@@ -220,7 +220,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
}
case QTYPE_QLIST: {
ToJsonIterState s;
- QList *val = qobject_to_qlist(obj);
+ QList *val = qobject_to(QList, obj);
s.count = 0;
s.str = str;
@@ -238,7 +238,7 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
break;
}
case QTYPE_QBOOL: {
- QBool *val = qobject_to_qbool(obj);
+ QBool *val = qobject_to(QBool, obj);
if (qbool_get_bool(val)) {
qstring_append(str, "true");
diff --git a/qobject/qlist.c b/qobject/qlist.c
index 613a95c12b..954fe98375 100644
--- a/qobject/qlist.c
+++ b/qobject/qlist.c
@@ -152,17 +152,6 @@ size_t qlist_size(const QList *qlist)
}
/**
- * qobject_to_qlist(): Convert a QObject into a QList
- */
-QList *qobject_to_qlist(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QLIST) {
- return NULL;
- }
- return container_of(obj, QList, base);
-}
-
-/**
* qlist_is_equal(): Test whether the two QLists are equal
*
* In order to be considered equal, the respective two objects at each
@@ -173,8 +162,8 @@ QList *qobject_to_qlist(const QObject *obj)
*/
bool qlist_is_equal(const QObject *x, const QObject *y)
{
- const QList *list_x = qobject_to_qlist(x);
- const QList *list_y = qobject_to_qlist(y);
+ const QList *list_x = qobject_to(QList, x);
+ const QList *list_y = qobject_to(QList, y);
const QListEntry *entry_x, *entry_y;
entry_x = qlist_first(list_x);
@@ -203,7 +192,7 @@ void qlist_destroy_obj(QObject *obj)
QListEntry *entry, *next_entry;
assert(obj != NULL);
- qlist = qobject_to_qlist(obj);
+ qlist = qobject_to(QList, obj);
QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) {
QTAILQ_REMOVE(&qlist->head, entry, next);
diff --git a/qobject/qlit.c b/qobject/qlit.c
index 948e0b860c..be8332136c 100644
--- a/qobject/qlit.c
+++ b/qobject/qlit.c
@@ -21,6 +21,7 @@
#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qnull.h"
static bool qlit_equal_qdict(const QLitObject *lhs, const QDict *qdict)
{
@@ -68,16 +69,16 @@ bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs)
switch (lhs->type) {
case QTYPE_QBOOL:
- return lhs->value.qbool == qbool_get_bool(qobject_to_qbool(rhs));
+ return lhs->value.qbool == qbool_get_bool(qobject_to(QBool, rhs));
case QTYPE_QNUM:
- return lhs->value.qnum == qnum_get_int(qobject_to_qnum(rhs));
+ return lhs->value.qnum == qnum_get_int(qobject_to(QNum, rhs));
case QTYPE_QSTRING:
return (strcmp(lhs->value.qstr,
- qstring_get_str(qobject_to_qstring(rhs))) == 0);
+ qstring_get_str(qobject_to(QString, rhs))) == 0);
case QTYPE_QDICT:
- return qlit_equal_qdict(lhs, qobject_to_qdict(rhs));
+ return qlit_equal_qdict(lhs, qobject_to(QDict, rhs));
case QTYPE_QLIST:
- return qlit_equal_qlist(lhs, qobject_to_qlist(rhs));
+ return qlit_equal_qlist(lhs, qobject_to(QList, rhs));
case QTYPE_QNULL:
return true;
default:
@@ -86,3 +87,39 @@ bool qlit_equal_qobject(const QLitObject *lhs, const QObject *rhs)
return false;
}
+
+QObject *qobject_from_qlit(const QLitObject *qlit)
+{
+ switch (qlit->type) {
+ case QTYPE_QNULL:
+ return QOBJECT(qnull());
+ case QTYPE_QNUM:
+ return QOBJECT(qnum_from_int(qlit->value.qnum));
+ case QTYPE_QSTRING:
+ return QOBJECT(qstring_from_str(qlit->value.qstr));
+ case QTYPE_QDICT: {
+ QDict *qdict = qdict_new();
+ QLitDictEntry *e;
+
+ for (e = qlit->value.qdict; e->key; e++) {
+ qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
+ }
+ return QOBJECT(qdict);
+ }
+ case QTYPE_QLIST: {
+ QList *qlist = qlist_new();
+ QLitObject *e;
+
+ for (e = qlit->value.qlist; e->type != QTYPE_NONE; e++) {
+ qlist_append_obj(qlist, qobject_from_qlit(e));
+ }
+ return QOBJECT(qlist);
+ }
+ case QTYPE_QBOOL:
+ return QOBJECT(qbool_from_bool(qlit->value.qbool));
+ default:
+ assert(0);
+ }
+
+ return NULL;
+}
diff --git a/qobject/qnum.c b/qobject/qnum.c
index 60c395c1bc..1501c82832 100644
--- a/qobject/qnum.c
+++ b/qobject/qnum.c
@@ -200,17 +200,6 @@ char *qnum_to_string(QNum *qn)
}
/**
- * qobject_to_qnum(): Convert a QObject into a QNum
- */
-QNum *qobject_to_qnum(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QNUM) {
- return NULL;
- }
- return container_of(obj, QNum, base);
-}
-
-/**
* qnum_is_equal(): Test whether the two QNums are equal
*
* Negative integers are never considered equal to unsigned integers,
@@ -221,8 +210,8 @@ QNum *qobject_to_qnum(const QObject *obj)
*/
bool qnum_is_equal(const QObject *x, const QObject *y)
{
- QNum *num_x = qobject_to_qnum(x);
- QNum *num_y = qobject_to_qnum(y);
+ QNum *num_x = qobject_to(QNum, x);
+ QNum *num_y = qobject_to(QNum, y);
switch (num_x->kind) {
case QNUM_I64:
@@ -271,5 +260,5 @@ bool qnum_is_equal(const QObject *x, const QObject *y)
void qnum_destroy_obj(QObject *obj)
{
assert(obj != NULL);
- g_free(qobject_to_qnum(obj));
+ g_free(qobject_to(QNum, obj));
}
diff --git a/qobject/qstring.c b/qobject/qstring.c
index 05b4bbc2d6..afca54b47a 100644
--- a/qobject/qstring.c
+++ b/qobject/qstring.c
@@ -106,17 +106,6 @@ void qstring_append_chr(QString *qstring, int c)
}
/**
- * qobject_to_qstring(): Convert a QObject to a QString
- */
-QString *qobject_to_qstring(const QObject *obj)
-{
- if (!obj || qobject_type(obj) != QTYPE_QSTRING) {
- return NULL;
- }
- return container_of(obj, QString, base);
-}
-
-/**
* qstring_get_str(): Return a pointer to the stored string
*
* NOTE: Should be used with caution, if the object is deallocated
@@ -128,12 +117,33 @@ const char *qstring_get_str(const QString *qstring)
}
/**
+ * qstring_get_try_str(): Return a pointer to the stored string
+ *
+ * NOTE: will return NULL if qstring is not provided.
+ */
+const char *qstring_get_try_str(const QString *qstring)
+{
+ return qstring ? qstring_get_str(qstring) : NULL;
+}
+
+/**
+ * qobject_get_try_str(): Return a pointer to the corresponding string
+ *
+ * NOTE: the string will only be returned if the object is valid, and
+ * its type is QString, otherwise NULL is returned.
+ */
+const char *qobject_get_try_str(const QObject *qstring)
+{
+ return qstring_get_try_str(qobject_to(QString, qstring));
+}
+
+/**
* qstring_is_equal(): Test whether the two QStrings are equal
*/
bool qstring_is_equal(const QObject *x, const QObject *y)
{
- return !strcmp(qobject_to_qstring(x)->string,
- qobject_to_qstring(y)->string);
+ return !strcmp(qobject_to(QString, x)->string,
+ qobject_to(QString, y)->string);
}
/**
@@ -145,7 +155,7 @@ void qstring_destroy_obj(QObject *obj)
QString *qs;
assert(obj != NULL);
- qs = qobject_to_qstring(obj);
+ qs = qobject_to(QString, obj);
g_free(qs->string);
g_free(qs);
}
diff --git a/qom/cpu.c b/qom/cpu.c
index e42d9a7f9e..92599f3541 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -67,37 +67,6 @@ CPUState *cpu_create(const char *typename)
return cpu;
}
-const char *cpu_parse_cpu_model(const char *typename, const char *cpu_model)
-{
- ObjectClass *oc;
- CPUClass *cc;
- gchar **model_pieces;
- const char *cpu_type;
-
- model_pieces = g_strsplit(cpu_model, ",", 2);
-
- oc = cpu_class_by_name(typename, model_pieces[0]);
- if (oc == NULL) {
- error_report("unable to find CPU model '%s'", model_pieces[0]);
- g_strfreev(model_pieces);
- exit(EXIT_FAILURE);
- }
-
- cpu_type = object_class_get_name(oc);
- cc = CPU_CLASS(oc);
- cc->parse_features(cpu_type, model_pieces[1], &error_fatal);
- g_strfreev(model_pieces);
- return cpu_type;
-}
-
-CPUState *cpu_generic_init(const char *typename, const char *cpu_model)
-{
- /* TODO: all callers of cpu_generic_init() need to be converted to
- * call cpu_parse_features() only once, before calling cpu_generic_init().
- */
- return cpu_create(cpu_parse_cpu_model(typename, cpu_model));
-}
-
bool cpu_paging_enabled(const CPUState *cpu)
{
CPUClass *cc = CPU_GET_CLASS(cpu);
@@ -317,41 +286,24 @@ static bool cpu_common_has_work(CPUState *cs)
ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model)
{
- CPUClass *cc;
-
- if (!cpu_model) {
- return NULL;
- }
- cc = CPU_CLASS(object_class_by_name(typename));
+ CPUClass *cc = CPU_CLASS(object_class_by_name(typename));
+ assert(cpu_model && cc->class_by_name);
return cc->class_by_name(cpu_model);
}
-static ObjectClass *cpu_common_class_by_name(const char *cpu_model)
-{
- return NULL;
-}
-
static void cpu_common_parse_features(const char *typename, char *features,
Error **errp)
{
- char *featurestr; /* Single "key=value" string being parsed */
char *val;
static bool cpu_globals_initialized;
+ /* Single "key=value" string being parsed */
+ char *featurestr = features ? strtok(features, ",") : NULL;
- /* TODO: all callers of ->parse_features() need to be changed to
- * call it only once, so we can remove this check (or change it
- * to assert(!cpu_globals_initialized).
- * Current callers of ->parse_features() are:
- * - cpu_generic_init()
- */
- if (cpu_globals_initialized) {
- return;
- }
+ /* should be called only once, catch invalid users */
+ assert(!cpu_globals_initialized);
cpu_globals_initialized = true;
- featurestr = features ? strtok(features, ",") : NULL;
-
while (featurestr) {
val = strchr(featurestr, '=');
if (val) {
@@ -457,7 +409,6 @@ static void cpu_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
CPUClass *k = CPU_CLASS(klass);
- k->class_by_name = cpu_common_class_by_name;
k->parse_features = cpu_common_parse_features;
k->reset = cpu_common_reset;
k->get_arch_id = cpu_common_get_arch_id;
diff --git a/qom/object.c b/qom/object.c
index 755ad03819..467795189c 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -891,6 +891,19 @@ GSList *object_class_get_list(const char *implements_type,
return list;
}
+static gint object_class_cmp(gconstpointer a, gconstpointer b)
+{
+ return strcasecmp(object_class_get_name((ObjectClass *)a),
+ object_class_get_name((ObjectClass *)b));
+}
+
+GSList *object_class_get_list_sorted(const char *implements_type,
+ bool include_abstract)
+{
+ return g_slist_sort(object_class_get_list(implements_type, include_abstract),
+ object_class_cmp);
+}
+
void object_ref(Object *obj)
{
if (!obj) {
@@ -1123,18 +1136,15 @@ char *object_property_get_str(Object *obj, const char *name,
Error **errp)
{
QObject *ret = object_property_get_qobject(obj, name, errp);
- QString *qstring;
char *retval;
if (!ret) {
return NULL;
}
- qstring = qobject_to_qstring(ret);
- if (!qstring) {
+
+ retval = g_strdup(qobject_get_try_str(ret));
+ if (!retval) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string");
- retval = NULL;
- } else {
- retval = g_strdup(qstring_get_str(qstring));
}
qobject_decref(ret);
@@ -1190,7 +1200,7 @@ bool object_property_get_bool(Object *obj, const char *name,
if (!ret) {
return false;
}
- qbool = qobject_to_qbool(ret);
+ qbool = qobject_to(QBool, ret);
if (!qbool) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean");
retval = false;
@@ -1222,7 +1232,7 @@ int64_t object_property_get_int(Object *obj, const char *name,
return -1;
}
- qnum = qobject_to_qnum(ret);
+ qnum = qobject_to(QNum, ret);
if (!qnum || !qnum_get_try_int(qnum, &retval)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int");
retval = -1;
@@ -1251,7 +1261,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name,
if (!ret) {
return 0;
}
- qnum = qobject_to_qnum(ret);
+ qnum = qobject_to(QNum, ret);
if (!qnum || !qnum_get_try_uint(qnum, &retval)) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "uint");
retval = 0;
diff --git a/replay/replay-audio.c b/replay/replay-audio.c
index 3d837434d4..b113836de4 100644
--- a/replay/replay-audio.c
+++ b/replay/replay-audio.c
@@ -19,20 +19,17 @@
void replay_audio_out(int *played)
{
if (replay_mode == REPLAY_MODE_RECORD) {
+ g_assert(replay_mutex_locked());
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_AUDIO_OUT);
replay_put_dword(*played);
- replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
replay_account_executed_instructions();
- replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_OUT)) {
*played = replay_get_dword();
replay_finish_event();
- replay_mutex_unlock();
} else {
- replay_mutex_unlock();
error_report("Missing audio out event in the replay log");
abort();
}
@@ -44,8 +41,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
int pos;
uint64_t left, right;
if (replay_mode == REPLAY_MODE_RECORD) {
+ g_assert(replay_mutex_locked());
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_AUDIO_IN);
replay_put_dword(*recorded);
replay_put_dword(*wpos);
@@ -55,10 +52,9 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
replay_put_qword(left);
replay_put_qword(right);
}
- replay_mutex_unlock();
} else if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
replay_account_executed_instructions();
- replay_mutex_lock();
if (replay_next_event_is(EVENT_AUDIO_IN)) {
*recorded = replay_get_dword();
*wpos = replay_get_dword();
@@ -69,9 +65,7 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
audio_sample_from_uint64(samples, pos, left, right);
}
replay_finish_event();
- replay_mutex_unlock();
} else {
- replay_mutex_unlock();
error_report("Missing audio in event in the replay log");
abort();
}
diff --git a/replay/replay-char.c b/replay/replay-char.c
index cbf7c04a9f..736cc8c2e6 100755
--- a/replay/replay-char.c
+++ b/replay/replay-char.c
@@ -96,25 +96,24 @@ void *replay_event_char_read_load(void)
void replay_char_write_event_save(int res, int offset)
{
+ g_assert(replay_mutex_locked());
+
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_CHAR_WRITE);
replay_put_dword(res);
replay_put_dword(offset);
- replay_mutex_unlock();
}
void replay_char_write_event_load(int *res, int *offset)
{
+ g_assert(replay_mutex_locked());
+
replay_account_executed_instructions();
- replay_mutex_lock();
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
*res = replay_get_dword();
*offset = replay_get_dword();
replay_finish_event();
- replay_mutex_unlock();
} else {
- replay_mutex_unlock();
error_report("Missing character write event in the replay log");
exit(1);
}
@@ -122,23 +121,21 @@ void replay_char_write_event_load(int *res, int *offset)
int replay_char_read_all_load(uint8_t *buf)
{
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
+
if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
size_t size;
int res;
replay_get_array(buf, &size);
replay_finish_event();
- replay_mutex_unlock();
res = (int)size;
assert(res >= 0);
return res;
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
int res = replay_get_dword();
replay_finish_event();
- replay_mutex_unlock();
return res;
} else {
- replay_mutex_unlock();
error_report("Missing character read all event in the replay log");
exit(1);
}
@@ -146,19 +143,17 @@ int replay_char_read_all_load(uint8_t *buf)
void replay_char_read_all_save_error(int res)
{
+ g_assert(replay_mutex_locked());
assert(res < 0);
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
replay_put_dword(res);
- replay_mutex_unlock();
}
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
{
+ g_assert(replay_mutex_locked());
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_CHAR_READ_ALL);
replay_put_array(buf, offset);
- replay_mutex_unlock();
}
diff --git a/replay/replay-events.c b/replay/replay-events.c
index 94a6dcccfc..707de3867b 100644
--- a/replay/replay-events.c
+++ b/replay/replay-events.c
@@ -27,10 +27,6 @@ typedef struct Event {
} Event;
static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
-static unsigned int read_event_kind = -1;
-static uint64_t read_id = -1;
-static int read_checkpoint = -1;
-
static bool events_enabled;
/* Functions */
@@ -67,7 +63,9 @@ static void replay_run_event(Event *event)
void replay_enable_events(void)
{
- events_enabled = true;
+ if (replay_mode != REPLAY_MODE_NONE) {
+ events_enabled = true;
+ }
}
bool replay_has_events(void)
@@ -77,16 +75,14 @@ bool replay_has_events(void)
void replay_flush_events(void)
{
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
+
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
- replay_mutex_unlock();
replay_run_event(event);
- replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
- replay_mutex_unlock();
}
void replay_disable_events(void)
@@ -100,14 +96,14 @@ void replay_disable_events(void)
void replay_clear_events(void)
{
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
+
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
- replay_mutex_unlock();
}
/*! Adds specified async event to the queue */
@@ -134,14 +130,13 @@ void replay_add_event(ReplayAsyncEventKind event_kind,
event->opaque2 = opaque2;
event->id = id;
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
QTAILQ_INSERT_TAIL(&events_list, event, events);
- replay_mutex_unlock();
}
void replay_bh_schedule_event(QEMUBH *bh)
{
- if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
+ if (events_enabled) {
uint64_t id = replay_get_current_step();
replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
} else {
@@ -161,7 +156,7 @@ void replay_add_input_sync_event(void)
void replay_block_event(QEMUBH *bh, uint64_t id)
{
- if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
+ if (events_enabled) {
replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
} else {
qemu_bh_schedule(bh);
@@ -205,13 +200,12 @@ static void replay_save_event(Event *event, int checkpoint)
/* Called with replay mutex locked */
void replay_save_events(int checkpoint)
{
+ g_assert(replay_mutex_locked());
+ g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_save_event(event, checkpoint);
-
- replay_mutex_unlock();
replay_run_event(event);
- replay_mutex_lock();
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
@@ -220,58 +214,60 @@ void replay_save_events(int checkpoint)
static Event *replay_read_event(int checkpoint)
{
Event *event;
- if (read_event_kind == -1) {
- read_checkpoint = replay_get_byte();
- read_event_kind = replay_get_byte();
- read_id = -1;
+ if (replay_state.read_event_kind == -1) {
+ replay_state.read_event_checkpoint = replay_get_byte();
+ replay_state.read_event_kind = replay_get_byte();
+ replay_state.read_event_id = -1;
replay_check_error();
}
- if (checkpoint != read_checkpoint) {
+ if (checkpoint != replay_state.read_event_checkpoint) {
return NULL;
}
/* Events that has not to be in the queue */
- switch (read_event_kind) {
+ switch (replay_state.read_event_kind) {
case REPLAY_ASYNC_EVENT_BH:
- if (read_id == -1) {
- read_id = replay_get_qword();
+ if (replay_state.read_event_id == -1) {
+ replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_INPUT:
event = g_malloc0(sizeof(Event));
- event->event_kind = read_event_kind;
+ event->event_kind = replay_state.read_event_kind;
event->opaque = replay_read_input_event();
return event;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
event = g_malloc0(sizeof(Event));
- event->event_kind = read_event_kind;
+ event->event_kind = replay_state.read_event_kind;
event->opaque = 0;
return event;
case REPLAY_ASYNC_EVENT_CHAR_READ:
event = g_malloc0(sizeof(Event));
- event->event_kind = read_event_kind;
+ event->event_kind = replay_state.read_event_kind;
event->opaque = replay_event_char_read_load();
return event;
case REPLAY_ASYNC_EVENT_BLOCK:
- if (read_id == -1) {
- read_id = replay_get_qword();
+ if (replay_state.read_event_id == -1) {
+ replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_NET:
event = g_malloc0(sizeof(Event));
- event->event_kind = read_event_kind;
+ event->event_kind = replay_state.read_event_kind;
event->opaque = replay_event_net_load();
return event;
default:
- error_report("Unknown ID %d of replay event", read_event_kind);
+ error_report("Unknown ID %d of replay event",
+ replay_state.read_event_kind);
exit(1);
break;
}
QTAILQ_FOREACH(event, &events_list, events) {
- if (event->event_kind == read_event_kind
- && (read_id == -1 || read_id == event->id)) {
+ if (event->event_kind == replay_state.read_event_kind
+ && (replay_state.read_event_id == -1
+ || replay_state.read_event_id == event->id)) {
break;
}
}
@@ -290,24 +286,23 @@ static Event *replay_read_event(int checkpoint)
/* Called with replay mutex locked */
void replay_read_events(int checkpoint)
{
+ g_assert(replay_mutex_locked());
while (replay_state.data_kind == EVENT_ASYNC) {
Event *event = replay_read_event(checkpoint);
if (!event) {
break;
}
- replay_mutex_unlock();
+ replay_finish_event();
+ replay_state.read_event_kind = -1;
replay_run_event(event);
- replay_mutex_lock();
g_free(event);
- replay_finish_event();
- read_event_kind = -1;
}
}
void replay_init_events(void)
{
- read_event_kind = -1;
+ replay_state.read_event_kind = -1;
}
void replay_finish_events(void)
diff --git a/replay/replay-internal.c b/replay/replay-internal.c
index fca8514012..b077cb5fd5 100644
--- a/replay/replay-internal.c
+++ b/replay/replay-internal.c
@@ -24,12 +24,23 @@
static QemuMutex lock;
/* File for replay writing */
+static bool write_error;
FILE *replay_file;
+static void replay_write_error(void)
+{
+ if (!write_error) {
+ error_report("replay write error");
+ write_error = true;
+ }
+}
+
void replay_put_byte(uint8_t byte)
{
if (replay_file) {
- putc(byte, replay_file);
+ if (putc(byte, replay_file) == EOF) {
+ replay_write_error();
+ }
}
}
@@ -62,7 +73,9 @@ void replay_put_array(const uint8_t *buf, size_t size)
{
if (replay_file) {
replay_put_dword(size);
- fwrite(buf, 1, size, replay_file);
+ if (fwrite(buf, 1, size, replay_file) != size) {
+ replay_write_error();
+ }
}
}
@@ -169,31 +182,46 @@ void replay_finish_event(void)
replay_fetch_data_kind();
}
+static __thread bool replay_locked;
+
void replay_mutex_init(void)
{
qemu_mutex_init(&lock);
+ /* Hold the mutex while we start-up */
+ qemu_mutex_lock(&lock);
+ replay_locked = true;
}
-void replay_mutex_destroy(void)
+bool replay_mutex_locked(void)
{
- qemu_mutex_destroy(&lock);
+ return replay_locked;
}
+/* Ordering constraints, replay_lock must be taken before BQL */
void replay_mutex_lock(void)
{
- qemu_mutex_lock(&lock);
+ if (replay_mode != REPLAY_MODE_NONE) {
+ g_assert(!qemu_mutex_iothread_locked());
+ g_assert(!replay_mutex_locked());
+ qemu_mutex_lock(&lock);
+ replay_locked = true;
+ }
}
void replay_mutex_unlock(void)
{
- qemu_mutex_unlock(&lock);
+ if (replay_mode != REPLAY_MODE_NONE) {
+ g_assert(replay_mutex_locked());
+ replay_locked = false;
+ qemu_mutex_unlock(&lock);
+ }
}
/*! Saves cached instructions. */
void replay_save_instructions(void)
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
int diff = (int)(replay_get_current_step() - replay_state.current_step);
/* Time can only go forward */
@@ -204,6 +232,5 @@ void replay_save_instructions(void)
replay_put_dword(diff);
replay_state.current_step += diff;
}
- replay_mutex_unlock();
}
}
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
index 3ebb19912a..ac4b27b674 100644
--- a/replay/replay-internal.h
+++ b/replay/replay-internal.h
@@ -12,7 +12,7 @@
*
*/
-
+/* Any changes to order/number of events will need to bump REPLAY_VERSION */
enum ReplayEvents {
/* for instruction event */
EVENT_INSTRUCTION,
@@ -78,6 +78,14 @@ typedef struct ReplayState {
This counter is global, because requests from different
block devices should not get overlapping ids. */
uint64_t block_request_id;
+ /*! Prior value of the host clock */
+ uint64_t host_clock_last;
+ /*! Asynchronous event type read from the log */
+ int32_t read_event_kind;
+ /*! Asynchronous event id read from the log */
+ uint64_t read_event_id;
+ /*! Asynchronous event checkpoint id read from the log */
+ int32_t read_event_checkpoint;
} ReplayState;
extern ReplayState replay_state;
@@ -98,12 +106,11 @@ int64_t replay_get_qword(void);
void replay_get_array(uint8_t *buf, size_t *size);
void replay_get_array_alloc(uint8_t **buf, size_t *size);
-/* Mutex functions for protecting replay log file */
+/* Mutex functions for protecting replay log file and ensuring
+ * synchronisation between vCPU and main-loop threads. */
void replay_mutex_init(void);
-void replay_mutex_destroy(void);
-void replay_mutex_lock(void);
-void replay_mutex_unlock(void);
+bool replay_mutex_locked(void);
/*! Checks error status of the file. */
void replay_check_error(void);
diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c
index b2e10769a6..2ab85cfc60 100644
--- a/replay/replay-snapshot.c
+++ b/replay/replay-snapshot.c
@@ -25,6 +25,7 @@ static int replay_pre_save(void *opaque)
{
ReplayState *state = opaque;
state->file_offset = ftell(replay_file);
+ state->host_clock_last = qemu_clock_get_last(QEMU_CLOCK_HOST);
return 0;
}
@@ -33,6 +34,7 @@ static int replay_post_load(void *opaque, int version_id)
{
ReplayState *state = opaque;
fseek(replay_file, state->file_offset, SEEK_SET);
+ qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last);
/* If this was a vmstate, saved in recording mode,
we need to initialize replay data fields. */
replay_fetch_data_kind();
@@ -54,6 +56,10 @@ static const VMStateDescription vmstate_replay = {
VMSTATE_UINT32(has_unread_data, ReplayState),
VMSTATE_UINT64(file_offset, ReplayState),
VMSTATE_UINT64(block_request_id, ReplayState),
+ VMSTATE_UINT64(host_clock_last, ReplayState),
+ VMSTATE_INT32(read_event_kind, ReplayState),
+ VMSTATE_UINT64(read_event_id, ReplayState),
+ VMSTATE_INT32(read_event_checkpoint, ReplayState),
VMSTATE_END_OF_LIST()
},
};
@@ -83,3 +89,9 @@ void replay_vmstate_init(void)
}
}
}
+
+bool replay_can_snapshot(void)
+{
+ return replay_mode == REPLAY_MODE_NONE
+ || !replay_has_events();
+}
diff --git a/replay/replay-time.c b/replay/replay-time.c
index f70382a88f..6a7565ec8d 100644
--- a/replay/replay-time.c
+++ b/replay/replay-time.c
@@ -17,13 +17,13 @@
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock)
{
- replay_save_instructions();
if (replay_file) {
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
+
+ replay_save_instructions();
replay_put_event(EVENT_CLOCK + kind);
replay_put_qword(clock);
- replay_mutex_unlock();
}
return clock;
@@ -46,16 +46,16 @@ void replay_read_next_clock(ReplayClockKind kind)
/*! Reads next clock event from the input. */
int64_t replay_read_clock(ReplayClockKind kind)
{
+ g_assert(replay_file && replay_mutex_locked());
+
replay_account_executed_instructions();
if (replay_file) {
int64_t ret;
- replay_mutex_lock();
if (replay_next_event_is(EVENT_CLOCK + kind)) {
replay_read_next_clock(kind);
}
ret = replay_state.cached_clock[kind];
- replay_mutex_unlock();
return ret;
}
diff --git a/replay/replay.c b/replay/replay.c
index 7a23c62d61..8228261401 100644
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -22,7 +22,7 @@
/* Current version of the replay mechanism.
Increase it when file format changes. */
-#define REPLAY_VERSION 0xe02006
+#define REPLAY_VERSION 0xe02007
/* Size of replay log header */
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
@@ -81,7 +81,7 @@ int replay_get_instructions(void)
void replay_account_executed_instructions(void)
{
if (replay_mode == REPLAY_MODE_PLAY) {
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
if (replay_state.instructions_count > 0) {
int count = (int)(replay_get_current_step()
- replay_state.current_step);
@@ -100,24 +100,22 @@ void replay_account_executed_instructions(void)
qemu_notify_event();
}
}
- replay_mutex_unlock();
}
}
bool replay_exception(void)
{
+
if (replay_mode == REPLAY_MODE_RECORD) {
+ g_assert(replay_mutex_locked());
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_EXCEPTION);
- replay_mutex_unlock();
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
bool res = replay_has_exception();
if (res) {
- replay_mutex_lock();
replay_finish_event();
- replay_mutex_unlock();
}
return res;
}
@@ -129,10 +127,9 @@ bool replay_has_exception(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
replay_account_executed_instructions();
- replay_mutex_lock();
res = replay_next_event_is(EVENT_EXCEPTION);
- replay_mutex_unlock();
}
return res;
@@ -141,17 +138,15 @@ bool replay_has_exception(void)
bool replay_interrupt(void)
{
if (replay_mode == REPLAY_MODE_RECORD) {
+ g_assert(replay_mutex_locked());
replay_save_instructions();
- replay_mutex_lock();
replay_put_event(EVENT_INTERRUPT);
- replay_mutex_unlock();
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
bool res = replay_has_interrupt();
if (res) {
- replay_mutex_lock();
replay_finish_event();
- replay_mutex_unlock();
}
return res;
}
@@ -163,10 +158,9 @@ bool replay_has_interrupt(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
replay_account_executed_instructions();
- replay_mutex_lock();
res = replay_next_event_is(EVENT_INTERRUPT);
- replay_mutex_unlock();
}
return res;
}
@@ -174,25 +168,35 @@ bool replay_has_interrupt(void)
void replay_shutdown_request(ShutdownCause cause)
{
if (replay_mode == REPLAY_MODE_RECORD) {
- replay_mutex_lock();
+ g_assert(replay_mutex_locked());
replay_put_event(EVENT_SHUTDOWN + cause);
- replay_mutex_unlock();
}
}
bool replay_checkpoint(ReplayCheckpoint checkpoint)
{
bool res = false;
+ static bool in_checkpoint;
assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
- replay_save_instructions();
if (!replay_file) {
return true;
}
- replay_mutex_lock();
+ if (in_checkpoint) {
+ /* If we are already in checkpoint, then there is no need
+ for additional synchronization.
+ Recursion occurs when HW event modifies timers.
+ Timer modification may invoke the checkpoint and
+ proceed to recursion. */
+ return true;
+ }
+ in_checkpoint = true;
+
+ replay_save_instructions();
if (replay_mode == REPLAY_MODE_PLAY) {
+ g_assert(replay_mutex_locked());
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
replay_finish_event();
} else if (replay_state.data_kind != EVENT_ASYNC) {
@@ -205,12 +209,18 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
checkpoint were processed */
res = replay_state.data_kind != EVENT_ASYNC;
} else if (replay_mode == REPLAY_MODE_RECORD) {
+ g_assert(replay_mutex_locked());
replay_put_event(EVENT_CHECKPOINT + checkpoint);
- replay_save_events(checkpoint);
+ /* This checkpoint belongs to several threads.
+ Processing events from different threads is
+ non-deterministic */
+ if (checkpoint != CHECKPOINT_CLOCK_WARP_START) {
+ replay_save_events(checkpoint);
+ }
res = true;
}
out:
- replay_mutex_unlock();
+ in_checkpoint = false;
return res;
}
@@ -233,8 +243,6 @@ static void replay_enable(const char *fname, int mode)
atexit(replay_finish);
- replay_mutex_init();
-
replay_file = fopen(fname, fmode);
if (replay_file == NULL) {
fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
@@ -242,8 +250,9 @@ static void replay_enable(const char *fname, int mode)
}
replay_filename = g_strdup(fname);
-
replay_mode = mode;
+ replay_mutex_init();
+
replay_state.data_kind = -1;
replay_state.instructions_count = 0;
replay_state.current_step = 0;
@@ -358,7 +367,6 @@ void replay_finish(void)
replay_snapshot = NULL;
replay_finish_events();
- replay_mutex_destroy();
}
void replay_add_blocker(Error *reason)
diff --git a/roms/seabios b/roms/seabios
-Subproject 63451fca13c75870e1703eb3e20584d91179aeb
+Subproject 0551a4be2ce599fb60e478b4c15e06ab6587822
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index d1fe79bcc4..57daae05ea 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1447,9 +1447,10 @@ sub process {
# check we are in a valid source file if not then ignore this hunk
next if ($realfile !~ /$SrcFile/);
-#90 column limit
+#90 column limit; exempt URLs, if no other words on line
if ($line =~ /^\+/ &&
!($line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) &&
+ !($rawline =~ /^[^[:alnum:]]*https?:\S*$/) &&
$length > 80)
{
if ($length > 90) {
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 21a7e0dbe6..0c5da3a54d 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -193,10 +193,18 @@ out:
return ret
-def gen_register_command(name, success_response):
- options = 'QCO_NO_OPTIONS'
+def gen_register_command(name, success_response, allow_oob):
+ options = []
+
if not success_response:
- options = 'QCO_NO_SUCCESS_RESP'
+ options += ['QCO_NO_SUCCESS_RESP']
+ if allow_oob:
+ options += ['QCO_ALLOW_OOB']
+
+ if not options:
+ options = ['QCO_NO_OPTIONS']
+
+ options = " | ".join(options)
ret = mcgen('''
qmp_register_command(cmds, "%(name)s",
@@ -268,7 +276,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
genc.add(gen_registry(self._regy, self._prefix))
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
if not gen:
return
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
@@ -277,7 +285,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
self._genc.add(gen_marshal_output(ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
- self._regy += gen_register_command(name, success_response)
+ self._regy += gen_register_command(name, success_response, allow_oob)
def gen_commands(schema, output_dir, prefix):
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 97e9060b67..2c05e3c284 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -921,7 +921,8 @@ def check_exprs(exprs):
elif 'command' in expr:
meta = 'command'
check_keys(expr_elem, 'command', [],
- ['data', 'returns', 'gen', 'success-response', 'boxed'])
+ ['data', 'returns', 'gen', 'success-response',
+ 'boxed', 'allow-oob'])
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
@@ -1044,7 +1045,7 @@ class QAPISchemaVisitor(object):
pass
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
pass
def visit_event(self, name, info, arg_type, boxed):
@@ -1421,7 +1422,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
QAPISchemaEntity.__init__(self, name, info, doc)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
@@ -1432,6 +1433,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.gen = gen
self.success_response = success_response
self.boxed = boxed
+ self.allow_oob = allow_oob
def check(self, schema):
if self._arg_type_name:
@@ -1455,7 +1457,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
def visit(self, visitor):
visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type,
- self.gen, self.success_response, self.boxed)
+ self.gen, self.success_response,
+ self.boxed, self.allow_oob)
class QAPISchemaEvent(QAPISchemaEntity):
@@ -1674,6 +1677,7 @@ class QAPISchema(object):
gen = expr.get('gen', True)
success_response = expr.get('success-response', True)
boxed = expr.get('boxed', False)
+ allow_oob = expr.get('allow-oob', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
@@ -1681,7 +1685,8 @@ class QAPISchema(object):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
- gen, success_response, boxed))
+ gen, success_response,
+ boxed, allow_oob))
def _def_event(self, expr, info, doc):
name = expr['event']
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index 0ea68bf813..9b312b2c51 100644
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -134,10 +134,9 @@ def texi_enum_value(value):
def texi_member(member, suffix=''):
"""Format a table of members item for an object type member"""
typ = member.type.doc_type()
- return '@item @code{%s%s%s}%s%s\n' % (
- member.name,
- ': ' if typ else '',
- typ if typ else '',
+ membertype = ': ' + typ if typ else ''
+ return '@item @code{%s%s}%s%s\n' % (
+ member.name, membertype,
' (optional)' if member.optional else '',
suffix)
@@ -228,7 +227,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
body=texi_entity(doc, 'Members')))
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index f66c397fb0..f9e67e8227 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -13,26 +13,38 @@ See the COPYING file in the top-level directory.
from qapi.common import *
-# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
-# TODO try to use json.dumps() once we get unstuck
-def to_json(obj, level=0):
+def to_qlit(obj, level=0, suppress_first_indent=False):
+
+ def indent(level):
+ return level * 4 * ' '
+
+ ret = ''
+ if not suppress_first_indent:
+ ret += indent(level)
if obj is None:
- ret = 'null'
+ ret += 'QLIT_QNULL'
elif isinstance(obj, str):
- ret = '"' + obj.replace('"', r'\"') + '"'
+ ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
elif isinstance(obj, list):
- elts = [to_json(elt, level + 1)
+ elts = [to_qlit(elt, level + 1)
for elt in obj]
- ret = '[' + ', '.join(elts) + ']'
+ elts.append(indent(level + 1) + "{}")
+ ret += 'QLIT_QLIST(((QLitObject[]) {\n'
+ ret += ',\n'.join(elts) + '\n'
+ ret += indent(level) + '}))'
elif isinstance(obj, dict):
- elts = ['"%s": %s' % (key.replace('"', r'\"'),
- to_json(obj[key], level + 1))
- for key in sorted(obj.keys())]
- ret = '{' + ', '.join(elts) + '}'
+ elts = []
+ for key, value in sorted(obj.items()):
+ elts.append(indent(level + 1) + '{ %s, %s }' %
+ (to_c_string(key), to_qlit(value, level + 1, True)))
+ elts.append(indent(level + 1) + '{}')
+ ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
+ ret += ',\n'.join(elts) + '\n'
+ ret += indent(level) + '}))'
+ elif isinstance(obj, bool):
+ ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
else:
assert False # not implemented
- if level == 1:
- ret = '\n' + ret
return ret
@@ -48,7 +60,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
' * QAPI/QMP schema introspection', __doc__)
self._unmask = unmask
self._schema = None
- self._jsons = []
+ self._qlits = []
self._used_types = []
self._name_map = {}
self._genc.add(mcgen('''
@@ -63,27 +75,27 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
def visit_end(self):
# visit the types that are actually used
- jsons = self._jsons
- self._jsons = []
+ qlits = self._qlits
+ self._qlits = []
for typ in self._used_types:
typ.visit(self)
# generate C
# TODO can generate awfully long lines
- jsons.extend(self._jsons)
- name = c_name(self._prefix, protect=False) + 'qmp_schema_json'
+ qlits.extend(self._qlits)
+ name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
self._genh.add(mcgen('''
-extern const char %(c_name)s[];
+#include "qapi/qmp/qlit.h"
+
+extern const QLitObject %(c_name)s;
''',
c_name=c_name(name)))
- lines = to_json(jsons).split('\n')
- c_string = '\n '.join([to_c_string(line) for line in lines])
self._genc.add(mcgen('''
-const char %(c_name)s[] = %(c_string)s;
+const QLitObject %(c_name)s = %(c_string)s;
''',
c_name=c_name(name),
- c_string=c_string))
+ c_string=to_qlit(qlits)))
self._schema = None
- self._jsons = []
+ self._qlits = []
self._used_types = []
self._name_map = {}
@@ -117,12 +129,12 @@ const char %(c_name)s[] = %(c_string)s;
return '[' + self._use_type(typ.element_type) + ']'
return self._name(typ.name)
- def _gen_json(self, name, mtype, obj):
+ def _gen_qlit(self, name, mtype, obj):
if mtype not in ('command', 'event', 'builtin', 'array'):
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
- self._jsons.append(obj)
+ self._qlits.append(obj)
def _gen_member(self, member):
ret = {'name': member.name, 'type': self._use_type(member.type)}
@@ -138,38 +150,39 @@ const char %(c_name)s[] = %(c_string)s;
return {'case': variant.name, 'type': self._use_type(variant.type)}
def visit_builtin_type(self, name, info, json_type):
- self._gen_json(name, 'builtin', {'json-type': json_type})
+ self._gen_qlit(name, 'builtin', {'json-type': json_type})
def visit_enum_type(self, name, info, values, prefix):
- self._gen_json(name, 'enum', {'values': values})
+ self._gen_qlit(name, 'enum', {'values': values})
def visit_array_type(self, name, info, element_type):
element = self._use_type(element_type)
- self._gen_json('[' + element + ']', 'array', {'element-type': element})
+ self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
def visit_object_type_flat(self, name, info, members, variants):
obj = {'members': [self._gen_member(m) for m in members]}
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
- self._gen_json(name, 'object', obj)
+ self._gen_qlit(name, 'object', obj)
def visit_alternate_type(self, name, info, variants):
- self._gen_json(name, 'alternate',
+ self._gen_qlit(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
- self._gen_json(name, 'command',
+ self._gen_qlit(name, 'command',
{'arg-type': self._use_type(arg_type),
- 'ret-type': self._use_type(ret_type)})
+ 'ret-type': self._use_type(ret_type),
+ 'allow-oob': allow_oob})
def visit_event(self, name, info, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
- self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
+ self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
def gen_introspect(schema, output_dir, prefix, opt_unmask):
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index bdb21bdd58..f39ad344fc 100755
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -1,10 +1,10 @@
#!/bin/sh
-# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA
+# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA/Xtensa
# program execution by the kernel
qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \
mips mipsel mipsn32 mipsn32el mips64 mips64el \
-sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64"
+sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb"
i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'
i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
@@ -108,6 +108,14 @@ riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x
riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
riscv64_family=riscv
+xtensa_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5e\x00'
+xtensa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+xtensa_family=xtensa
+
+xtensaeb_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5e'
+xtensaeb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
+xtensaeb_family=xtensaeb
+
qemu_get_family() {
cpu=${HOST_ARCH:-$(uname -m)}
case "$cpu" in
@@ -154,7 +162,8 @@ Usage: qemu-binfmt-conf.sh [--qemu-path PATH][--debian][--systemd CPU]
instead generate update-binfmts templates
--systemd: don't write into /proc,
instead generate file for systemd-binfmt.service
- for the given CPU
+ for the given CPU. If CPU is "ALL", generate a
+ file for all known cpus
--exportdir: define where to write configuration files
(default: $SYSTEMDDIR or $DEBIANDIR)
--credential: if yes, credential and security tokens are
@@ -301,18 +310,20 @@ while true ; do
EXPORTDIR=${EXPORTDIR:-$SYSTEMDDIR}
shift
# check given cpu is in the supported CPU list
- for cpu in ${qemu_target_list} ; do
+ if [ "$1" != "ALL" ] ; then
+ for cpu in ${qemu_target_list} ; do
+ if [ "$cpu" = "$1" ] ; then
+ break
+ fi
+ done
+
if [ "$cpu" = "$1" ] ; then
- break
+ qemu_target_list="$1"
+ else
+ echo "ERROR: unknown CPU \"$1\"" 1>&2
+ usage
+ exit 1
fi
- done
-
- if [ "$cpu" = "$1" ] ; then
- qemu_target_list="$1"
- else
- echo "ERROR: unknown CPU \"$1\"" 1>&2
- usage
- exit 1
fi
;;
-Q|--qemu-path)
diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py
new file mode 100755
index 0000000000..e274086277
--- /dev/null
+++ b/scripts/replay-dump.py
@@ -0,0 +1,308 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Dump the contents of a recorded execution stream
+#
+# Copyright (c) 2017 Alex Bennée <alex.bennee@linaro.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+import argparse
+import struct
+from collections import namedtuple
+
+# This mirrors some of the global replay state which some of the
+# stream loading refers to. Some decoders may read the next event so
+# we need handle that case. Calling reuse_event will ensure the next
+# event is read from the cache rather than advancing the file.
+
+class ReplayState(object):
+ def __init__(self):
+ self.event = -1
+ self.event_count = 0
+ self.already_read = False
+ self.current_checkpoint = 0
+ self.checkpoint = 0
+
+ def set_event(self, ev):
+ self.event = ev
+ self.event_count += 1
+
+ def get_event(self):
+ self.already_read = False
+ return self.event
+
+ def reuse_event(self, ev):
+ self.event = ev
+ self.already_read = True
+
+ def set_checkpoint(self):
+ self.checkpoint = self.event - self.checkpoint_start
+
+ def get_checkpoint(self):
+ return self.checkpoint
+
+replay_state = ReplayState()
+
+# Simple read functions that mirror replay-internal.c
+# The file-stream is big-endian and manually written out a byte at a time.
+
+def read_byte(fin):
+ "Read a single byte"
+ return struct.unpack('>B', fin.read(1))[0]
+
+def read_event(fin):
+ "Read a single byte event, but save some state"
+ if replay_state.already_read:
+ return replay_state.get_event()
+ else:
+ replay_state.set_event(read_byte(fin))
+ return replay_state.event
+
+def read_word(fin):
+ "Read a 16 bit word"
+ return struct.unpack('>H', fin.read(2))[0]
+
+def read_dword(fin):
+ "Read a 32 bit word"
+ return struct.unpack('>I', fin.read(4))[0]
+
+def read_qword(fin):
+ "Read a 64 bit word"
+ return struct.unpack('>Q', fin.read(8))[0]
+
+# Generic decoder structure
+Decoder = namedtuple("Decoder", "eid name fn")
+
+def call_decode(table, index, dumpfile):
+ "Search decode table for next step"
+ decoder = next((d for d in table if d.eid == index), None)
+ if not decoder:
+ print "Could not decode index: %d" % (index)
+ print "Entry is: %s" % (decoder)
+ print "Decode Table is:\n%s" % (table)
+ return False
+ else:
+ return decoder.fn(decoder.eid, decoder.name, dumpfile)
+
+# Print event
+def print_event(eid, name, string=None, event_count=None):
+ "Print event with count"
+ if not event_count:
+ event_count = replay_state.event_count
+
+ if string:
+ print "%d:%s(%d) %s" % (event_count, name, eid, string)
+ else:
+ print "%d:%s(%d)" % (event_count, name, eid)
+
+
+# Decoders for each event type
+
+def decode_unimp(eid, name, _unused_dumpfile):
+ "Unimplimented decoder, will trigger exit"
+ print "%s not handled - will now stop" % (name)
+ return False
+
+# Checkpoint decoder
+def swallow_async_qword(eid, name, dumpfile):
+ "Swallow a qword of data without looking at it"
+ step_id = read_qword(dumpfile)
+ print " %s(%d) @ %d" % (name, eid, step_id)
+ return True
+
+async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword),
+ Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp),
+ Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp),
+ Decoder(3, "REPLAY_ASYNC_CHAR_READ", decode_unimp),
+ Decoder(4, "REPLAY_ASYNC_EVENT_BLOCK", decode_unimp),
+ Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp),
+]
+# See replay_read_events/replay_read_event
+def decode_async(eid, name, dumpfile):
+ """Decode an ASYNC event"""
+
+ print_event(eid, name)
+
+ async_event_kind = read_byte(dumpfile)
+ async_event_checkpoint = read_byte(dumpfile)
+
+ if async_event_checkpoint != replay_state.current_checkpoint:
+ print " mismatch between checkpoint %d and async data %d" % (
+ replay_state.current_checkpoint, async_event_checkpoint)
+ return True
+
+ return call_decode(async_decode_table, async_event_kind, dumpfile)
+
+
+def decode_instruction(eid, name, dumpfile):
+ ins_diff = read_dword(dumpfile)
+ print_event(eid, name, "0x%x" % (ins_diff))
+ return True
+
+def decode_audio_out(eid, name, dumpfile):
+ audio_data = read_dword(dumpfile)
+ print_event(eid, name, "%d" % (audio_data))
+ return True
+
+def decode_checkpoint(eid, name, dumpfile):
+ """Decode a checkpoint.
+
+ Checkpoints contain a series of async events with their own specific data.
+ """
+ replay_state.set_checkpoint()
+ # save event count as we peek ahead
+ event_number = replay_state.event_count
+ next_event = read_event(dumpfile)
+
+ # if the next event is EVENT_ASYNC there are a bunch of
+ # async events to read, otherwise we are done
+ if next_event != 3:
+ print_event(eid, name, "no additional data", event_number)
+ else:
+ print_event(eid, name, "more data follows", event_number)
+
+ replay_state.reuse_event(next_event)
+ return True
+
+def decode_checkpoint_init(eid, name, dumpfile):
+ print_event(eid, name)
+ return True
+
+def decode_interrupt(eid, name, dumpfile):
+ print_event(eid, name)
+ return True
+
+def decode_clock(eid, name, dumpfile):
+ clock_data = read_qword(dumpfile)
+ print_event(eid, name, "0x%x" % (clock_data))
+ return True
+
+
+# pre-MTTCG merge
+v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
+ Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
+ Decoder(2, "EVENT_EXCEPTION", decode_unimp),
+ Decoder(3, "EVENT_ASYNC", decode_async),
+ Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
+ Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
+ Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
+ Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
+ Decoder(8, "EVENT_CLOCK_HOST", decode_clock),
+ Decoder(9, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
+ Decoder(10, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
+ Decoder(11, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
+ Decoder(12, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
+ Decoder(13, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
+ Decoder(14, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
+ Decoder(15, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
+ Decoder(16, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
+ Decoder(17, "EVENT_CP_INIT", decode_checkpoint_init),
+ Decoder(18, "EVENT_CP_RESET", decode_checkpoint),
+]
+
+# post-MTTCG merge, AUDIO support added
+v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
+ Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
+ Decoder(2, "EVENT_EXCEPTION", decode_unimp),
+ Decoder(3, "EVENT_ASYNC", decode_async),
+ Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
+ Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
+ Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
+ Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
+ Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out),
+ Decoder(9, "EVENT_AUDIO_IN", decode_unimp),
+ Decoder(10, "EVENT_CLOCK_HOST", decode_clock),
+ Decoder(11, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
+ Decoder(12, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
+ Decoder(13, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
+ Decoder(14, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
+ Decoder(15, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
+ Decoder(16, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
+ Decoder(17, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
+ Decoder(18, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
+ Decoder(19, "EVENT_CP_INIT", decode_checkpoint_init),
+ Decoder(20, "EVENT_CP_RESET", decode_checkpoint),
+]
+
+# Shutdown cause added
+v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
+ Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
+ Decoder(2, "EVENT_EXCEPTION", decode_unimp),
+ Decoder(3, "EVENT_ASYNC", decode_async),
+ Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
+ Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp),
+ Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp),
+ Decoder(7, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp),
+ Decoder(8, "EVENT_SHUTDOWN_HOST_UI", decode_unimp),
+ Decoder(9, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp),
+ Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp),
+ Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp),
+ Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp),
+ Decoder(13, "EVENT_CHAR_WRITE", decode_unimp),
+ Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp),
+ Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
+ Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out),
+ Decoder(17, "EVENT_AUDIO_IN", decode_unimp),
+ Decoder(18, "EVENT_CLOCK_HOST", decode_clock),
+ Decoder(19, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
+ Decoder(20, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
+ Decoder(21, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
+ Decoder(22, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
+ Decoder(23, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
+ Decoder(24, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
+ Decoder(25, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
+ Decoder(26, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
+ Decoder(27, "EVENT_CP_INIT", decode_checkpoint_init),
+ Decoder(28, "EVENT_CP_RESET", decode_checkpoint),
+]
+
+def parse_arguments():
+ "Grab arguments for script"
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-f", "--file", help='record/replay dump to read from',
+ required=True)
+ return parser.parse_args()
+
+def decode_file(filename):
+ "Decode a record/replay dump"
+ dumpfile = open(filename, "rb")
+
+ # read and throwaway the header
+ version = read_dword(dumpfile)
+ junk = read_qword(dumpfile)
+
+ print "HEADER: version 0x%x" % (version)
+
+ if version == 0xe02007:
+ event_decode_table = v7_event_table
+ replay_state.checkpoint_start = 12
+ elif version == 0xe02006:
+ event_decode_table = v6_event_table
+ replay_state.checkpoint_start = 12
+ else:
+ event_decode_table = v5_event_table
+ replay_state.checkpoint_start = 10
+
+ try:
+ decode_ok = True
+ while decode_ok:
+ event = read_event(dumpfile)
+ decode_ok = call_decode(event_decode_table, event, dumpfile)
+ finally:
+ dumpfile.close()
+
+if __name__ == "__main__":
+ args = parse_arguments()
+ decode_file(args.file)
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index d18e2f1ffa..5b1d8dcdf4 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -39,6 +39,7 @@ cp_portable() {
-e 'input-event-codes' \
-e 'sys/' \
-e 'pvrdma_verbs' \
+ -e 'drm.h' \
-e 'limits' \
-e 'linux/kernel' \
-e 'linux/sysinfo' \
@@ -59,6 +60,8 @@ cp_portable() {
-e 's/__bitwise//' \
-e 's/__attribute__((packed))/QEMU_PACKED/' \
-e 's/__inline__/inline/' \
+ -e 's/__BITS_PER_LONG/HOST_LONG_BITS/' \
+ -e '/\"drm.h\"/d' \
-e '/sys\/ioctl.h/d' \
-e 's/SW_MAX/SW_MAX_/' \
-e 's/atomic_t/int/' \
@@ -106,6 +109,8 @@ for arch in $ARCHLIST; do
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/"
fi
if [ $arch = arm ]; then
cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/"
@@ -125,7 +130,7 @@ done
rm -rf "$output/linux-headers/linux"
mkdir -p "$output/linux-headers/linux"
for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \
- psci.h userfaultfd.h; do
+ psci.h psp-sev.h userfaultfd.h; do
cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
done
rm -rf "$output/linux-headers/asm-generic"
@@ -158,6 +163,9 @@ for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \
"$tmpdir/include/linux/sysinfo.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" \
+ "$output/include/standard-headers/drm"
rm -rf "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma"
mkdir -p "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma"
diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c
index 9fe615c73c..3facbba170 100644
--- a/scsi/qemu-pr-helper.c
+++ b/scsi/qemu-pr-helper.c
@@ -102,7 +102,7 @@ QEMU_HELP_BOTTOM "\n"
static void version(const char *name)
{
printf(
-"%s " QEMU_VERSION QEMU_PKGVERSION "\n"
+"%s " QEMU_FULL_VERSION "\n"
"Written by Paolo Bonzini.\n"
"\n"
QEMU_COPYRIGHT "\n"
diff --git a/stubs/machine-init-done.c b/stubs/machine-init-done.c
index 9a0d62514f..4121f1709b 100644
--- a/stubs/machine-init-done.c
+++ b/stubs/machine-init-done.c
@@ -2,6 +2,8 @@
#include "qemu-common.h"
#include "sysemu/sysemu.h"
+bool machine_init_done = true;
+
void qemu_add_machine_init_done_notifier(Notifier *notify)
{
}
diff --git a/stubs/replay.c b/stubs/replay.c
index 9c8aa48c9c..04279abb2c 100644
--- a/stubs/replay.c
+++ b/stubs/replay.c
@@ -72,3 +72,11 @@ uint64_t blkreplay_next_id(void)
{
return 0;
}
+
+void replay_mutex_lock(void)
+{
+}
+
+void replay_mutex_unlock(void)
+{
+}
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index 55675ce419..b08078e7fc 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -71,18 +71,6 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp)
acc->parent_realize(dev, errp);
}
-/* Sort alphabetically by type name. */
-static gint alpha_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
- ObjectClass *class_a = (ObjectClass *)a;
- ObjectClass *class_b = (ObjectClass *)b;
- const char *name_a, *name_b;
-
- name_a = object_class_get_name(class_a);
- name_b = object_class_get_name(class_b);
- return strcmp(name_a, name_b);
-}
-
static void alpha_cpu_list_entry(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
@@ -100,8 +88,7 @@ void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf)
};
GSList *list;
- list = object_class_get_list(TYPE_ALPHA_CPU, false);
- list = g_slist_sort(list, alpha_cpu_list_compare);
+ list = object_class_get_list_sorted(TYPE_ALPHA_CPU, false);
(*cpu_fprintf)(f, "Available CPUs:\n");
g_slist_foreach(list, alpha_cpu_list_entry, &s);
g_slist_free(list);
diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h
index a79fc2e780..7b50be785d 100644
--- a/target/alpha/cpu.h
+++ b/target/alpha/cpu.h
@@ -466,10 +466,9 @@ enum {
void alpha_translate_init(void);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_ALPHA_CPU, cpu_model)
-
#define ALPHA_CPU_TYPE_SUFFIX "-" TYPE_ALPHA_CPU
#define ALPHA_CPU_TYPE_NAME(model) model ALPHA_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_ALPHA_CPU
void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf);
/* you can call this signal handler from your SIGBUS and SIGSEGV
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 1e7e1f8a7e..19a0c03f9b 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -2302,10 +2302,9 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx,
return unmasked || pstate_unmasked;
}
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_ARM_CPU, cpu_model)
-
#define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU
#define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_ARM_CPU
#define cpu_signal_handler cpu_arm_signal_handler
#define cpu_list arm_cpu_list
diff --git a/target/cris/cpu.h b/target/cris/cpu.h
index 764b35cbae..8bb1dbc989 100644
--- a/target/cris/cpu.h
+++ b/target/cris/cpu.h
@@ -267,10 +267,9 @@ enum {
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_CRIS_CPU, cpu_model)
-
#define CRIS_CPU_TYPE_SUFFIX "-" TYPE_CRIS_CPU
#define CRIS_CPU_TYPE_NAME(name) (name CRIS_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_CRIS_CPU
#define cpu_signal_handler cpu_cris_signal_handler
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 969f628f0a..c261b6b090 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -110,18 +110,6 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
#endif
}
-/* Sort hppabetically by type name. */
-static gint hppa_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
- ObjectClass *class_a = (ObjectClass *)a;
- ObjectClass *class_b = (ObjectClass *)b;
- const char *name_a, *name_b;
-
- name_a = object_class_get_name(class_a);
- name_b = object_class_get_name(class_b);
- return strcmp(name_a, name_b);
-}
-
static void hppa_cpu_list_entry(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
@@ -138,8 +126,7 @@ void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
};
GSList *list;
- list = object_class_get_list(TYPE_HPPA_CPU, false);
- list = g_slist_sort(list, hppa_cpu_list_compare);
+ list = object_class_get_list_sorted(TYPE_HPPA_CPU, false);
(*cpu_fprintf)(f, "Available CPUs:\n");
g_slist_foreach(list, hppa_cpu_list_entry, &s);
g_slist_free(list);
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index c88d844938..19dd12a93e 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -266,7 +266,7 @@ static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch)
void hppa_translate_init(void);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_HPPA_CPU, cpu_model)
+#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU
void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs
index f5c6ef20a7..04678f5503 100644
--- a/target/i386/Makefile.objs
+++ b/target/i386/Makefile.objs
@@ -5,7 +5,9 @@ obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
obj-$(CONFIG_KVM) += kvm.o hyperv.o
+obj-$(CONFIG_SEV) += sev.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
+obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
# HAX support
ifdef CONFIG_WIN32
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ec1efd3a3c..555ae79d29 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -26,6 +26,7 @@
#include "sysemu/hvf.h"
#include "sysemu/cpus.h"
#include "kvm_i386.h"
+#include "sev_i386.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
@@ -194,6 +195,8 @@
* bit[02]: Support Single-Range Output scheme;
*/
#define INTEL_PT_MINIMAL_ECX 0x7
+/* generated packets which contain IP payloads have LIP values */
+#define INTEL_PT_IP_LIP (1 << 31)
#define INTEL_PT_ADDR_RANGES_NUM 0x2 /* Number of configurable address ranges */
#define INTEL_PT_ADDR_RANGES_NUM_MASK 0x3
#define INTEL_PT_MTC_BITMAP (0x0249 << 16) /* Support ART(0,3,6,9) */
@@ -780,13 +783,7 @@ static char *x86_cpu_type_name(const char *model_name)
static ObjectClass *x86_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
- char *typename;
-
- if (cpu_model == NULL) {
- return NULL;
- }
-
- typename = x86_cpu_type_name(cpu_model);
+ char *typename = x86_cpu_type_name(cpu_model);
oc = object_class_by_name(typename);
g_free(typename);
return oc;
@@ -3140,7 +3137,7 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type,
xc = x86_cpu_from_model(model->name,
model->has_props ?
- qobject_to_qdict(model->props) :
+ qobject_to(QDict, model->props) :
NULL, &err);
if (err) {
goto out;
@@ -3672,6 +3669,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
*ecx = 0;
*edx = 0;
break;
+ case 0x8000001F:
+ *eax = sev_enabled() ? 0x2 : 0;
+ *ebx = sev_get_cbit_position();
+ *ebx |= sev_get_reduced_phys_bits() << 6;
+ *ecx = 0;
+ *edx = 0;
+ break;
default:
/* reserved values: zero */
*eax = 0;
@@ -3705,6 +3709,7 @@ static void x86_cpu_reset(CPUState *s)
cpu_x86_update_cr0(env, 0x60000010);
env->a20_mask = ~0x0;
env->smbase = 0x30000;
+ env->msr_smi_count = 0;
env->idt.limit = 0xffff;
env->gdt.limit = 0xffff;
@@ -4101,6 +4106,11 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A);
}
+
+ /* SEV requires CPUID[0x8000001F] */
+ if (sev_enabled()) {
+ x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F);
+ }
}
/* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */
@@ -4159,7 +4169,8 @@ static int x86_cpu_filter_features(X86CPU *cpu)
((eax_1 & INTEL_PT_ADDR_RANGES_NUM_MASK) <
INTEL_PT_ADDR_RANGES_NUM) ||
((ebx_1 & (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP)) !=
- (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP))) {
+ (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP)) ||
+ (ecx_0 & INTEL_PT_IP_LIP)) {
/*
* Processor Trace capabilities aren't configurable, so if the
* host can't emulate the capabilities we report on
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0c3f51445e..78db1b833a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1,3 +1,4 @@
+
/*
* i386 virtual CPU header
*
@@ -359,6 +360,7 @@ typedef enum X86Seg {
#define MSR_P6_PERFCTR0 0xc1
#define MSR_IA32_SMBASE 0x9e
+#define MSR_SMI_COUNT 0x34
#define MSR_MTRRcap 0xfe
#define MSR_MTRRcap_VCNT 8
#define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8)
@@ -1142,6 +1144,7 @@ typedef struct CPUX86State {
uint64_t pat;
uint32_t smbase;
+ uint64_t msr_smi_count;
uint32_t pkru;
@@ -1586,10 +1589,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
#define PHYS_ADDR_MASK MAKE_64BIT_MASK(0, TCG_PHYS_ADDR_BITS)
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_X86_CPU, cpu_model)
-
#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU
#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_X86_CPU
#ifdef TARGET_X86_64
#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu64")
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index d996cca68b..d23fff12f5 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -92,6 +92,7 @@ static bool has_msr_hv_stimer;
static bool has_msr_hv_frequencies;
static bool has_msr_xss;
static bool has_msr_spec_ctrl;
+static bool has_msr_smi_count;
static uint32_t has_architectural_pmu_version;
static uint32_t num_architectural_pmu_gp_counters;
@@ -1151,6 +1152,9 @@ static int kvm_get_supported_msrs(KVMState *s)
case MSR_IA32_SMBASE:
has_msr_smbase = true;
break;
+ case MSR_SMI_COUNT:
+ has_msr_smi_count = true;
+ break;
case MSR_IA32_MISC_ENABLE:
has_msr_misc_enable = true;
break;
@@ -1660,6 +1664,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
if (has_msr_smbase) {
kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, env->smbase);
}
+ if (has_msr_smi_count) {
+ kvm_msr_entry_add(cpu, MSR_SMI_COUNT, env->msr_smi_count);
+ }
if (has_msr_bndcfgs) {
kvm_msr_entry_add(cpu, MSR_IA32_BNDCFGS, env->msr_bndcfgs);
}
@@ -2025,6 +2032,9 @@ static int kvm_get_msrs(X86CPU *cpu)
if (has_msr_smbase) {
kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, 0);
}
+ if (has_msr_smi_count) {
+ kvm_msr_entry_add(cpu, MSR_SMI_COUNT, 0);
+ }
if (has_msr_feature_control) {
kvm_msr_entry_add(cpu, MSR_IA32_FEATURE_CONTROL, 0);
}
@@ -2265,6 +2275,9 @@ static int kvm_get_msrs(X86CPU *cpu)
case MSR_IA32_SMBASE:
env->smbase = msrs[i].data;
break;
+ case MSR_SMI_COUNT:
+ env->msr_smi_count = msrs[i].data;
+ break;
case MSR_IA32_FEATURE_CONTROL:
env->msr_ia32_feature_control = msrs[i].data;
break;
diff --git a/target/i386/machine.c b/target/i386/machine.c
index c05fe6fb1a..bd2d82e91b 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -395,6 +395,25 @@ static const VMStateDescription vmstate_msr_tsc_adjust = {
}
};
+static bool msr_smi_count_needed(void *opaque)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+
+ return env->msr_smi_count != 0;
+}
+
+static const VMStateDescription vmstate_msr_smi_count = {
+ .name = "cpu/msr_smi_count",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = msr_smi_count_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.msr_smi_count, X86CPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static bool tscdeadline_needed(void *opaque)
{
X86CPU *cpu = opaque;
@@ -989,6 +1008,7 @@ VMStateDescription vmstate_x86_cpu = {
&vmstate_avx512,
&vmstate_xss,
&vmstate_tsc_khz,
+ &vmstate_msr_smi_count,
#ifdef TARGET_X86_64
&vmstate_pkru,
#endif
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 75429129fd..011419eba2 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -29,7 +29,11 @@
#include "qapi/qmp/qdict.h"
#include "hw/i386/pc.h"
#include "sysemu/kvm.h"
+#include "sysemu/sev.h"
#include "hmp.h"
+#include "qapi/error.h"
+#include "sev_i386.h"
+#include "qapi/qapi-commands-misc.h"
static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
@@ -661,3 +665,65 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict)
ioapic_dump_state(mon, qdict);
}
}
+
+SevInfo *qmp_query_sev(Error **errp)
+{
+ SevInfo *info;
+
+ info = sev_get_info();
+ if (!info) {
+ error_setg(errp, "SEV feature is not available");
+ return NULL;
+ }
+
+ return info;
+}
+
+void hmp_info_sev(Monitor *mon, const QDict *qdict)
+{
+ SevInfo *info = sev_get_info();
+
+ if (info && info->enabled) {
+ monitor_printf(mon, "handle: %d\n", info->handle);
+ monitor_printf(mon, "state: %s\n", SevState_str(info->state));
+ monitor_printf(mon, "build: %d\n", info->build_id);
+ monitor_printf(mon, "api version: %d.%d\n",
+ info->api_major, info->api_minor);
+ monitor_printf(mon, "debug: %s\n",
+ info->policy & SEV_POLICY_NODBG ? "off" : "on");
+ monitor_printf(mon, "key-sharing: %s\n",
+ info->policy & SEV_POLICY_NOKS ? "off" : "on");
+ } else {
+ monitor_printf(mon, "SEV is not enabled\n");
+ }
+}
+
+SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
+{
+ char *data;
+ SevLaunchMeasureInfo *info;
+
+ data = sev_get_launch_measurement();
+ if (!data) {
+ error_setg(errp, "Measurement is not available");
+ return NULL;
+ }
+
+ info = g_malloc0(sizeof(*info));
+ info->data = data;
+
+ return info;
+}
+
+SevCapability *qmp_query_sev_capabilities(Error **errp)
+{
+ SevCapability *data;
+
+ data = sev_get_capabilities();
+ if (!data) {
+ error_setg(errp, "SEV feature is not available");
+ return NULL;
+ }
+
+ return data;
+}
diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
new file mode 100644
index 0000000000..59a003a4eb
--- /dev/null
+++ b/target/i386/sev-stub.c
@@ -0,0 +1,51 @@
+/*
+ * QEMU SEV stub
+ *
+ * Copyright Advanced Micro Devices 2018
+ *
+ * Authors:
+ * Brijesh Singh <brijesh.singh@amd.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 "qemu-common.h"
+#include "sev_i386.h"
+
+SevInfo *sev_get_info(void)
+{
+ return NULL;
+}
+
+bool sev_enabled(void)
+{
+ return false;
+}
+
+uint64_t sev_get_me_mask(void)
+{
+ return ~0;
+}
+
+uint32_t sev_get_cbit_position(void)
+{
+ return 0;
+}
+
+uint32_t sev_get_reduced_phys_bits(void)
+{
+ return 0;
+}
+
+char *sev_get_launch_measurement(void)
+{
+ return NULL;
+}
+
+SevCapability *sev_get_capabilities(void)
+{
+ return NULL;
+}
diff --git a/target/i386/sev.c b/target/i386/sev.c
new file mode 100644
index 0000000000..019d84cef2
--- /dev/null
+++ b/target/i386/sev.c
@@ -0,0 +1,811 @@
+/*
+ * QEMU SEV support
+ *
+ * Copyright Advanced Micro Devices 2016-2018
+ *
+ * Author:
+ * Brijesh Singh <brijesh.singh@amd.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 <linux/kvm.h>
+#include <linux/psp-sev.h>
+
+#include <sys/ioctl.h>
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "qemu/base64.h"
+#include "sysemu/kvm.h"
+#include "sev_i386.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "migration/blocker.h"
+
+#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
+#define DEFAULT_SEV_DEVICE "/dev/sev"
+
+static SEVState *sev_state;
+static Error *sev_mig_blocker;
+
+static const char *const sev_fw_errlist[] = {
+ "",
+ "Platform state is invalid",
+ "Guest state is invalid",
+ "Platform configuration is invalid",
+ "Buffer too small",
+ "Platform is already owned",
+ "Certificate is invalid",
+ "Policy is not allowed",
+ "Guest is not active",
+ "Invalid address",
+ "Bad signature",
+ "Bad measurement",
+ "Asid is already owned",
+ "Invalid ASID",
+ "WBINVD is required",
+ "DF_FLUSH is required",
+ "Guest handle is invalid",
+ "Invalid command",
+ "Guest is active",
+ "Hardware error",
+ "Hardware unsafe",
+ "Feature not supported",
+ "Invalid parameter"
+};
+
+#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist)
+
+static int
+sev_ioctl(int fd, int cmd, void *data, int *error)
+{
+ int r;
+ struct kvm_sev_cmd input;
+
+ memset(&input, 0x0, sizeof(input));
+
+ input.id = cmd;
+ input.sev_fd = fd;
+ input.data = (__u64)(unsigned long)data;
+
+ r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input);
+
+ if (error) {
+ *error = input.error;
+ }
+
+ return r;
+}
+
+static int
+sev_platform_ioctl(int fd, int cmd, void *data, int *error)
+{
+ int r;
+ struct sev_issue_cmd arg;
+
+ arg.cmd = cmd;
+ arg.data = (unsigned long)data;
+ r = ioctl(fd, SEV_ISSUE_CMD, &arg);
+ if (error) {
+ *error = arg.error;
+ }
+
+ return r;
+}
+
+static const char *
+fw_error_to_str(int code)
+{
+ if (code < 0 || code >= SEV_FW_MAX_ERROR) {
+ return "unknown error";
+ }
+
+ return sev_fw_errlist[code];
+}
+
+static bool
+sev_check_state(SevState state)
+{
+ assert(sev_state);
+ return sev_state->state == state ? true : false;
+}
+
+static void
+sev_set_guest_state(SevState new_state)
+{
+ assert(new_state < SEV_STATE__MAX);
+ assert(sev_state);
+
+ trace_kvm_sev_change_state(SevState_str(sev_state->state),
+ SevState_str(new_state));
+ sev_state->state = new_state;
+}
+
+static void
+sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
+{
+ int r;
+ struct kvm_enc_region range;
+
+ range.addr = (__u64)(unsigned long)host;
+ range.size = size;
+
+ trace_kvm_memcrypt_register_region(host, size);
+ r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
+ if (r) {
+ error_report("%s: failed to register region (%p+%#zx) error '%s'",
+ __func__, host, size, strerror(errno));
+ exit(1);
+ }
+}
+
+static void
+sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size)
+{
+ int r;
+ struct kvm_enc_region range;
+
+ range.addr = (__u64)(unsigned long)host;
+ range.size = size;
+
+ trace_kvm_memcrypt_unregister_region(host, size);
+ r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
+ if (r) {
+ error_report("%s: failed to unregister region (%p+%#zx)",
+ __func__, host, size);
+ }
+}
+
+static struct RAMBlockNotifier sev_ram_notifier = {
+ .ram_block_added = sev_ram_block_added,
+ .ram_block_removed = sev_ram_block_removed,
+};
+
+static void
+qsev_guest_finalize(Object *obj)
+{
+}
+
+static char *
+qsev_guest_get_session_file(Object *obj, Error **errp)
+{
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+ return s->session_file ? g_strdup(s->session_file) : NULL;
+}
+
+static void
+qsev_guest_set_session_file(Object *obj, const char *value, Error **errp)
+{
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+ s->session_file = g_strdup(value);
+}
+
+static char *
+qsev_guest_get_dh_cert_file(Object *obj, Error **errp)
+{
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+ return g_strdup(s->dh_cert_file);
+}
+
+static void
+qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
+{
+ QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+ s->dh_cert_file = g_strdup(value);
+}
+
+static char *
+qsev_guest_get_sev_device(Object *obj, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ return g_strdup(sev->sev_device);
+}
+
+static void
+qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ sev->sev_device = g_strdup(value);
+}
+
+static void
+qsev_guest_class_init(ObjectClass *oc, void *data)
+{
+ object_class_property_add_str(oc, "sev-device",
+ qsev_guest_get_sev_device,
+ qsev_guest_set_sev_device,
+ NULL);
+ object_class_property_set_description(oc, "sev-device",
+ "SEV device to use", NULL);
+ object_class_property_add_str(oc, "dh-cert-file",
+ qsev_guest_get_dh_cert_file,
+ qsev_guest_set_dh_cert_file,
+ NULL);
+ object_class_property_set_description(oc, "dh-cert-file",
+ "guest owners DH certificate (encoded with base64)", NULL);
+ object_class_property_add_str(oc, "session-file",
+ qsev_guest_get_session_file,
+ qsev_guest_set_session_file,
+ NULL);
+ object_class_property_set_description(oc, "session-file",
+ "guest owners session parameters (encoded with base64)", NULL);
+}
+
+static void
+qsev_guest_set_handle(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, errp);
+ sev->handle = value;
+}
+
+static void
+qsev_guest_set_policy(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, errp);
+ sev->policy = value;
+}
+
+static void
+qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, errp);
+ sev->cbitpos = value;
+}
+
+static void
+qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, errp);
+ sev->reduced_phys_bits = value;
+}
+
+static void
+qsev_guest_get_policy(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint32_t value;
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ value = sev->policy;
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_handle(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint32_t value;
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ value = sev->handle;
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint32_t value;
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ value = sev->cbitpos;
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ uint32_t value;
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ value = sev->reduced_phys_bits;
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_init(Object *obj)
+{
+ QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+ sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
+ sev->policy = DEFAULT_GUEST_POLICY;
+ object_property_add(obj, "policy", "uint32", qsev_guest_get_policy,
+ qsev_guest_set_policy, NULL, NULL, NULL);
+ object_property_add(obj, "handle", "uint32", qsev_guest_get_handle,
+ qsev_guest_set_handle, NULL, NULL, NULL);
+ object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos,
+ qsev_guest_set_cbitpos, NULL, NULL, NULL);
+ object_property_add(obj, "reduced-phys-bits", "uint32",
+ qsev_guest_get_reduced_phys_bits,
+ qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL);
+}
+
+/* sev guest info */
+static const TypeInfo qsev_guest_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_QSEV_GUEST_INFO,
+ .instance_size = sizeof(QSevGuestInfo),
+ .instance_finalize = qsev_guest_finalize,
+ .class_size = sizeof(QSevGuestInfoClass),
+ .class_init = qsev_guest_class_init,
+ .instance_init = qsev_guest_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+static QSevGuestInfo *
+lookup_sev_guest_info(const char *id)
+{
+ Object *obj;
+ QSevGuestInfo *info;
+
+ obj = object_resolve_path_component(object_get_objects_root(), id);
+ if (!obj) {
+ return NULL;
+ }
+
+ info = (QSevGuestInfo *)
+ object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO);
+ if (!info) {
+ return NULL;
+ }
+
+ return info;
+}
+
+bool
+sev_enabled(void)
+{
+ return sev_state ? true : false;
+}
+
+uint64_t
+sev_get_me_mask(void)
+{
+ return sev_state ? sev_state->me_mask : ~0;
+}
+
+uint32_t
+sev_get_cbit_position(void)
+{
+ return sev_state ? sev_state->cbitpos : 0;
+}
+
+uint32_t
+sev_get_reduced_phys_bits(void)
+{
+ return sev_state ? sev_state->reduced_phys_bits : 0;
+}
+
+SevInfo *
+sev_get_info(void)
+{
+ SevInfo *info;
+
+ info = g_new0(SevInfo, 1);
+ info->enabled = sev_state ? true : false;
+
+ if (info->enabled) {
+ info->api_major = sev_state->api_major;
+ info->api_minor = sev_state->api_minor;
+ info->build_id = sev_state->build_id;
+ info->policy = sev_state->policy;
+ info->state = sev_state->state;
+ info->handle = sev_state->handle;
+ }
+
+ return info;
+}
+
+static int
+sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
+ size_t *cert_chain_len)
+{
+ guchar *pdh_data, *cert_chain_data;
+ struct sev_user_data_pdh_cert_export export = {};
+ int err, r;
+
+ /* query the certificate length */
+ r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
+ if (r < 0) {
+ if (err != SEV_RET_INVALID_LEN) {
+ error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
+ return 1;
+ }
+ }
+
+ pdh_data = g_new(guchar, export.pdh_cert_len);
+ cert_chain_data = g_new(guchar, export.cert_chain_len);
+ export.pdh_cert_address = (unsigned long)pdh_data;
+ export.cert_chain_address = (unsigned long)cert_chain_data;
+
+ r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
+ if (r < 0) {
+ error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
+ goto e_free;
+ }
+
+ *pdh = pdh_data;
+ *pdh_len = export.pdh_cert_len;
+ *cert_chain = cert_chain_data;
+ *cert_chain_len = export.cert_chain_len;
+ return 0;
+
+e_free:
+ g_free(pdh_data);
+ g_free(cert_chain_data);
+ return 1;
+}
+
+SevCapability *
+sev_get_capabilities(void)
+{
+ SevCapability *cap;
+ guchar *pdh_data, *cert_chain_data;
+ size_t pdh_len = 0, cert_chain_len = 0;
+ uint32_t ebx;
+ int fd;
+
+ fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
+ if (fd < 0) {
+ error_report("%s: Failed to open %s '%s'", __func__,
+ DEFAULT_SEV_DEVICE, strerror(errno));
+ return NULL;
+ }
+
+ if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
+ &cert_chain_data, &cert_chain_len)) {
+ return NULL;
+ }
+
+ cap = g_new0(SevCapability, 1);
+ cap->pdh = g_base64_encode(pdh_data, pdh_len);
+ cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len);
+
+ host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
+ cap->cbitpos = ebx & 0x3f;
+
+ /*
+ * When SEV feature is enabled, we loose one bit in guest physical
+ * addressing.
+ */
+ cap->reduced_phys_bits = 1;
+
+ g_free(pdh_data);
+ g_free(cert_chain_data);
+
+ close(fd);
+ return cap;
+}
+
+static int
+sev_read_file_base64(const char *filename, guchar **data, gsize *len)
+{
+ gsize sz;
+ gchar *base64;
+ GError *error = NULL;
+
+ if (!g_file_get_contents(filename, &base64, &sz, &error)) {
+ error_report("failed to read '%s' (%s)", filename, error->message);
+ return -1;
+ }
+
+ *data = g_base64_decode(base64, len);
+ return 0;
+}
+
+static int
+sev_launch_start(SEVState *s)
+{
+ gsize sz;
+ int ret = 1;
+ int fw_error;
+ QSevGuestInfo *sev = s->sev_info;
+ struct kvm_sev_launch_start *start;
+ guchar *session = NULL, *dh_cert = NULL;
+
+ start = g_new0(struct kvm_sev_launch_start, 1);
+
+ start->handle = object_property_get_int(OBJECT(sev), "handle",
+ &error_abort);
+ start->policy = object_property_get_int(OBJECT(sev), "policy",
+ &error_abort);
+ if (sev->session_file) {
+ if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
+ return 1;
+ }
+ start->session_uaddr = (unsigned long)session;
+ start->session_len = sz;
+ }
+
+ if (sev->dh_cert_file) {
+ if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
+ return 1;
+ }
+ start->dh_uaddr = (unsigned long)dh_cert;
+ start->dh_len = sz;
+ }
+
+ trace_kvm_sev_launch_start(start->policy, session, dh_cert);
+ ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
+ if (ret < 0) {
+ error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
+ __func__, ret, fw_error, fw_error_to_str(fw_error));
+ return 1;
+ }
+
+ object_property_set_int(OBJECT(sev), start->handle, "handle",
+ &error_abort);
+ sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE);
+ s->handle = start->handle;
+ s->policy = start->policy;
+
+ g_free(start);
+ g_free(session);
+ g_free(dh_cert);
+
+ return 0;
+}
+
+static int
+sev_launch_update_data(uint8_t *addr, uint64_t len)
+{
+ int ret, fw_error;
+ struct kvm_sev_launch_update_data update;
+
+ if (!addr || !len) {
+ return 1;
+ }
+
+ update.uaddr = (__u64)(unsigned long)addr;
+ update.len = len;
+ trace_kvm_sev_launch_update_data(addr, len);
+ ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
+ &update, &fw_error);
+ if (ret) {
+ error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
+ __func__, ret, fw_error, fw_error_to_str(fw_error));
+ }
+
+ return ret;
+}
+
+static void
+sev_launch_get_measure(Notifier *notifier, void *unused)
+{
+ int ret, error;
+ guchar *data;
+ SEVState *s = sev_state;
+ struct kvm_sev_launch_measure *measurement;
+
+ if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
+ return;
+ }
+
+ measurement = g_new0(struct kvm_sev_launch_measure, 1);
+
+ /* query the measurement blob length */
+ ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+ measurement, &error);
+ if (!measurement->len) {
+ error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
+ __func__, ret, error, fw_error_to_str(errno));
+ goto free_measurement;
+ }
+
+ data = g_new0(guchar, measurement->len);
+ measurement->uaddr = (unsigned long)data;
+
+ /* get the measurement blob */
+ ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+ measurement, &error);
+ if (ret) {
+ error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
+ __func__, ret, error, fw_error_to_str(errno));
+ goto free_data;
+ }
+
+ sev_set_guest_state(SEV_STATE_LAUNCH_SECRET);
+
+ /* encode the measurement value and emit the event */
+ s->measurement = g_base64_encode(data, measurement->len);
+ trace_kvm_sev_launch_measurement(s->measurement);
+
+free_data:
+ g_free(data);
+free_measurement:
+ g_free(measurement);
+}
+
+char *
+sev_get_launch_measurement(void)
+{
+ if (sev_state &&
+ sev_state->state >= SEV_STATE_LAUNCH_SECRET) {
+ return g_strdup(sev_state->measurement);
+ }
+
+ return NULL;
+}
+
+static Notifier sev_machine_done_notify = {
+ .notify = sev_launch_get_measure,
+};
+
+static void
+sev_launch_finish(SEVState *s)
+{
+ int ret, error;
+ Error *local_err = NULL;
+
+ trace_kvm_sev_launch_finish();
+ ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error);
+ if (ret) {
+ error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'",
+ __func__, ret, error, fw_error_to_str(error));
+ exit(1);
+ }
+
+ sev_set_guest_state(SEV_STATE_RUNNING);
+
+ /* add migration blocker */
+ error_setg(&sev_mig_blocker,
+ "SEV: Migration is not implemented");
+ ret = migrate_add_blocker(sev_mig_blocker, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ error_free(sev_mig_blocker);
+ exit(1);
+ }
+}
+
+static void
+sev_vm_state_change(void *opaque, int running, RunState state)
+{
+ SEVState *s = opaque;
+
+ if (running) {
+ if (!sev_check_state(SEV_STATE_RUNNING)) {
+ sev_launch_finish(s);
+ }
+ }
+}
+
+void *
+sev_guest_init(const char *id)
+{
+ SEVState *s;
+ char *devname;
+ int ret, fw_error;
+ uint32_t ebx;
+ uint32_t host_cbitpos;
+ struct sev_user_data_status status = {};
+
+ s = g_new0(SEVState, 1);
+ s->sev_info = lookup_sev_guest_info(id);
+ if (!s->sev_info) {
+ error_report("%s: '%s' is not a valid '%s' object",
+ __func__, id, TYPE_QSEV_GUEST_INFO);
+ goto err;
+ }
+
+ sev_state = s;
+ s->state = SEV_STATE_UNINIT;
+
+ host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
+ host_cbitpos = ebx & 0x3f;
+
+ s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL);
+ if (host_cbitpos != s->cbitpos) {
+ error_report("%s: cbitpos check failed, host '%d' requested '%d'",
+ __func__, host_cbitpos, s->cbitpos);
+ goto err;
+ }
+
+ s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info),
+ "reduced-phys-bits", NULL);
+ if (s->reduced_phys_bits < 1) {
+ error_report("%s: reduced_phys_bits check failed, it should be >=1,"
+ "' requested '%d'", __func__, s->reduced_phys_bits);
+ goto err;
+ }
+
+ s->me_mask = ~(1UL << s->cbitpos);
+
+ devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL);
+ s->sev_fd = open(devname, O_RDWR);
+ if (s->sev_fd < 0) {
+ error_report("%s: Failed to open %s '%s'", __func__,
+ devname, strerror(errno));
+ goto err;
+ }
+ g_free(devname);
+
+ ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status,
+ &fw_error);
+ if (ret) {
+ error_report("%s: failed to get platform status ret=%d"
+ "fw_error='%d: %s'", __func__, ret, fw_error,
+ fw_error_to_str(fw_error));
+ goto err;
+ }
+ s->build_id = status.build;
+ s->api_major = status.api_major;
+ s->api_minor = status.api_minor;
+
+ trace_kvm_sev_init();
+ ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error);
+ if (ret) {
+ error_report("%s: failed to initialize ret=%d fw_error=%d '%s'",
+ __func__, ret, fw_error, fw_error_to_str(fw_error));
+ goto err;
+ }
+
+ ret = sev_launch_start(s);
+ if (ret) {
+ error_report("%s: failed to create encryption context", __func__);
+ goto err;
+ }
+
+ ram_block_notifier_add(&sev_ram_notifier);
+ qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+ qemu_add_vm_change_state_handler(sev_vm_state_change, s);
+
+ return s;
+err:
+ g_free(sev_state);
+ sev_state = NULL;
+ return NULL;
+}
+
+int
+sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
+{
+ assert(handle);
+
+ /* if SEV is in update state then encrypt the data else do nothing */
+ if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
+ return sev_launch_update_data(ptr, len);
+ }
+
+ return 0;
+}
+
+static void
+sev_register_types(void)
+{
+ type_register_static(&qsev_guest_info);
+}
+
+type_init(sev_register_types);
diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
new file mode 100644
index 0000000000..b8622dfb1e
--- /dev/null
+++ b/target/i386/sev_i386.h
@@ -0,0 +1,88 @@
+/*
+ * QEMU Secure Encrypted Virutualization (SEV) support
+ *
+ * Copyright: Advanced Micro Devices, 2016-2018
+ *
+ * Authors:
+ * Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_SEV_I386_H
+#define QEMU_SEV_I386_H
+
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+#include "sysemu/sev.h"
+#include "qemu/error-report.h"
+#include "qapi/qapi-commands-misc.h"
+
+#define SEV_POLICY_NODBG 0x1
+#define SEV_POLICY_NOKS 0x2
+#define SEV_POLICY_ES 0x4
+#define SEV_POLICY_NOSEND 0x8
+#define SEV_POLICY_DOMAIN 0x10
+#define SEV_POLICY_SEV 0x20
+
+#define TYPE_QSEV_GUEST_INFO "sev-guest"
+#define QSEV_GUEST_INFO(obj) \
+ OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO)
+
+extern bool sev_enabled(void);
+extern uint64_t sev_get_me_mask(void);
+extern SevInfo *sev_get_info(void);
+extern uint32_t sev_get_cbit_position(void);
+extern uint32_t sev_get_reduced_phys_bits(void);
+extern char *sev_get_launch_measurement(void);
+extern SevCapability *sev_get_capabilities(void);
+
+typedef struct QSevGuestInfo QSevGuestInfo;
+typedef struct QSevGuestInfoClass QSevGuestInfoClass;
+
+/**
+ * QSevGuestInfo:
+ *
+ * The QSevGuestInfo object is used for creating a SEV guest.
+ *
+ * # $QEMU \
+ * -object sev-guest,id=sev0 \
+ * -machine ...,memory-encryption=sev0
+ */
+struct QSevGuestInfo {
+ Object parent_obj;
+
+ char *sev_device;
+ uint32_t policy;
+ uint32_t handle;
+ char *dh_cert_file;
+ char *session_file;
+ uint32_t cbitpos;
+ uint32_t reduced_phys_bits;
+};
+
+struct QSevGuestInfoClass {
+ ObjectClass parent_class;
+};
+
+struct SEVState {
+ QSevGuestInfo *sev_info;
+ uint8_t api_major;
+ uint8_t api_minor;
+ uint8_t build_id;
+ uint32_t policy;
+ uint64_t me_mask;
+ uint32_t cbitpos;
+ uint32_t reduced_phys_bits;
+ uint32_t handle;
+ int sev_fd;
+ SevState state;
+ gchar *measurement;
+};
+
+typedef struct SEVState SEVState;
+
+#endif
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 3153fd4454..6a19a69af5 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -5,3 +5,13 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %"
kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d"
kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d"
kvm_x86_update_msi_routes(int num) "Updated %d MSI routes"
+
+# target/i386/sev.c
+kvm_sev_init(void) ""
+kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zu"
+kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zu"
+kvm_sev_change_state(const char *old, const char *new) "%s -> %s"
+kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p"
+kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIu64
+kvm_sev_launch_measurement(const char *value) "data %s"
+kvm_sev_launch_finish(void) ""
diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c
index 96c2499d0b..0003152469 100644
--- a/target/lm32/cpu.c
+++ b/target/lm32/cpu.c
@@ -32,18 +32,6 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value)
cpu->env.pc = value;
}
-/* Sort alphabetically by type name. */
-static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
- ObjectClass *class_a = (ObjectClass *)a;
- ObjectClass *class_b = (ObjectClass *)b;
- const char *name_a, *name_b;
-
- name_a = object_class_get_name(class_a);
- name_b = object_class_get_name(class_b);
- return strcmp(name_a, name_b);
-}
-
static void lm32_cpu_list_entry(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
@@ -65,8 +53,7 @@ void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf)
};
GSList *list;
- list = object_class_get_list(TYPE_LM32_CPU, false);
- list = g_slist_sort(list, lm32_cpu_list_compare);
+ list = object_class_get_list_sorted(TYPE_LM32_CPU, false);
(*cpu_fprintf)(f, "Available CPUs:\n");
g_slist_foreach(list, lm32_cpu_list_entry, &s);
g_slist_free(list);
diff --git a/target/lm32/cpu.h b/target/lm32/cpu.h
index ce0a2f24c4..66157eefe9 100644
--- a/target/lm32/cpu.h
+++ b/target/lm32/cpu.h
@@ -255,10 +255,9 @@ void lm32_watchpoint_insert(CPULM32State *env, int index, target_ulong address,
void lm32_watchpoint_remove(CPULM32State *env, int index);
bool lm32_cpu_do_semihosting(CPUState *cs);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_LM32_CPU, cpu_model)
-
#define LM32_CPU_TYPE_SUFFIX "-" TYPE_LM32_CPU
#define LM32_CPU_TYPE_NAME(model) model LM32_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_LM32_CPU
#define cpu_list lm32_cpu_list
#define cpu_signal_handler cpu_lm32_signal_handler
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 2259bf22dc..c63adf772f 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -527,10 +527,9 @@ enum {
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_M68K_CPU, cpu_model)
-
#define M68K_CPU_TYPE_SUFFIX "-" TYPE_M68K_CPU
#define M68K_CPU_TYPE_NAME(model) model M68K_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_M68K_CPU
#define cpu_signal_handler cpu_m68k_signal_handler
#define cpu_list m68k_cpu_list
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 62cbb0dff1..6eeffdf9bb 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -592,3 +592,64 @@ void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val)
{
res->d = floatx80_tentox(val->d, &env->fp_status);
}
+
+void HELPER(ftan)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_tan(val->d, &env->fp_status);
+}
+
+void HELPER(fsin)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_sin(val->d, &env->fp_status);
+}
+
+void HELPER(fcos)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_cos(val->d, &env->fp_status);
+}
+
+void HELPER(fsincos)(CPUM68KState *env, FPReg *res0, FPReg *res1, FPReg *val)
+{
+ floatx80 a = val->d;
+ /* If res0 and res1 specify the same floating-point data register,
+ * the sine result is stored in the register, and the cosine
+ * result is discarded.
+ */
+ res1->d = floatx80_cos(a, &env->fp_status);
+ res0->d = floatx80_sin(a, &env->fp_status);
+}
+
+void HELPER(fatan)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_atan(val->d, &env->fp_status);
+}
+
+void HELPER(fasin)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_asin(val->d, &env->fp_status);
+}
+
+void HELPER(facos)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_acos(val->d, &env->fp_status);
+}
+
+void HELPER(fatanh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_atanh(val->d, &env->fp_status);
+}
+
+void HELPER(ftanh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_tanh(val->d, &env->fp_status);
+}
+
+void HELPER(fsinh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_sinh(val->d, &env->fp_status);
+}
+
+void HELPER(fcosh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_cosh(val->d, &env->fp_status);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 9a9734c196..feee7be626 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -75,6 +75,17 @@ DEF_HELPER_3(flog2, void, env, fp, fp)
DEF_HELPER_3(fetox, void, env, fp, fp)
DEF_HELPER_3(ftwotox, void, env, fp, fp)
DEF_HELPER_3(ftentox, void, env, fp, fp)
+DEF_HELPER_3(ftan, void, env, fp, fp)
+DEF_HELPER_3(fsin, void, env, fp, fp)
+DEF_HELPER_3(fcos, void, env, fp, fp)
+DEF_HELPER_4(fsincos, void, env, fp, fp, fp)
+DEF_HELPER_3(fatan, void, env, fp, fp)
+DEF_HELPER_3(fasin, void, env, fp, fp)
+DEF_HELPER_3(facos, void, env, fp, fp)
+DEF_HELPER_3(fatanh, void, env, fp, fp)
+DEF_HELPER_3(ftanh, void, env, fp, fp)
+DEF_HELPER_3(fsinh, void, env, fp, fp)
+DEF_HELPER_3(fcosh, void, env, fp, fp)
DEF_HELPER_3(mac_move, void, env, i32, i32)
DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c
index 4bd5b9e6b7..dffb371c71 100644
--- a/target/m68k/softfloat.c
+++ b/target/m68k/softfloat.c
@@ -23,6 +23,10 @@
#include "fpu/softfloat-macros.h"
#include "softfloat_fpsp_tables.h"
+#define pi_exp 0x4000
+#define piby2_exp 0x3FFF
+#define pi_sig LIT64(0xc90fdaa22168c235)
+
static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
{
if (floatx80_is_signaling_nan(a, status)) {
@@ -1266,3 +1270,1636 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status)
return a;
}
}
+
+/*----------------------------------------------------------------------------
+ | Tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tan(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2;
+ float32 twoto63;
+ flag endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto tancont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ tancont:
+ if (n & 1) {
+ /* NODD */
+ fp1 = fp0; /* R */
+ fp0 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
+ status); /* Q4 */
+ fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
+ status); /* P3 */
+ fp3 = floatx80_mul(fp3, fp0, status); /* SQ4 */
+ fp2 = floatx80_mul(fp2, fp0, status); /* SP3 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF346F59B39BA65F), status),
+ status); /* Q3+SQ4 */
+ fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00));
+ fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
+ fp3 = floatx80_mul(fp3, fp0, status); /* S(Q3+SQ4) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(P2+SP3) */
+ fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA));
+ fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
+ fp3 = floatx80_mul(fp3, fp0, status); /* S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(P1+S(P2+SP3)) */
+ fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* RS(P1+S(P2+SP3)) */
+ fp0 = floatx80_mul(fp0, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
+ fp1 = floatx80_add(fp1, fp2, status); /* R+RS(P1+S(P2+SP3)) */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
+
+ xSign = extractFloatx80Sign(fp1);
+ xExp = extractFloatx80Exp(fp1);
+ xSig = extractFloatx80Frac(fp1);
+ xSign ^= 1;
+ fp1 = packFloatx80(xSign, xExp, xSig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
+ status); /* Q4 */
+ fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
+ status); /* P3 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* SQ4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* SP3 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF346F59B39BA65F), status),
+ status); /* Q3+SQ4 */
+ fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00));
+ fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S(Q3+SQ4) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S(P2+SP3) */
+ fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA));
+ fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S(P1+S(P2+SP3)) */
+ fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* RS(P1+S(P2+SP3)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
+ fp0 = floatx80_add(fp0, fp2, status); /* R+RS(P1+S(P2+SP3)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sin(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
+ float32 posneg1, twoto63;
+ flag adjn, endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ adjn = 0;
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto sincont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ /* SINSM */
+ fp0 = float32_to_floatx80(make_float32(0x3F800000),
+ status); /* 1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (adjn) {
+ /* COSTINY */
+ a = floatx80_sub(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status), status);
+ } else {
+ /* SINTINY */
+ a = floatx80_move(a, status);
+ }
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ sincont:
+ if ((n + adjn) & 1) {
+ /* COSPOLY */
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
+ status); /* B8 */
+ fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
+ status); /* B7 */
+
+ xSign = extractFloatx80Sign(fp0); /* X IS S */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ if (((n + adjn) >> 1) & 1) {
+ xSign ^= 1;
+ posneg1 = make_float32(0xBF800000); /* -1 */
+ } else {
+ xSign ^= 0;
+ posneg1 = make_float32(0x3F800000); /* 1 */
+ } /* X IS NOW R'= SGN*R */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3E21EED90612C972), status),
+ status); /* B6+TB8 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE927E4FB79D9FCF), status),
+ status); /* B5+TB7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A01A01D423), status),
+ status); /* B4+T(B6+TB8) */
+ fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438));
+ fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
+ fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E));
+ fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0xBF000000), status),
+ status); /* B1+T(B3+T(B5+TB7)) */
+ fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
+ fp0 = floatx80_add(fp0, fp1, status); /* [B1+T(B3+T(B5+TB7))]+
+ * [S(B2+T(B4+T(B6+TB8)))]
+ */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ /* SINPOLY */
+ xSign = extractFloatx80Sign(fp0); /* X IS R */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */
+
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
+ status); /* A7 */
+ fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
+ status); /* A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE5AE6452A118AE4), status),
+ status); /* A5+T*A7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5341531), status),
+ status); /* A4+T*A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF2A01A01A018B59), status),
+ status); /* A3+T(A5+TA7) */
+ fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF));
+ fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99));
+ fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
+ fp1 = floatx80_add(fp1, fp2,
+ status); /* [A1+T(A3+T(A5+TA7))]+
+ * [S(A2+T(A4+TA6))]
+ */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status); /* R'*S */
+ fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, x, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_cos(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
+ float32 posneg1, twoto63;
+ flag adjn, endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ adjn = 1;
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto sincont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ /* SINSM */
+ fp0 = float32_to_floatx80(make_float32(0x3F800000), status); /* 1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (adjn) {
+ /* COSTINY */
+ a = floatx80_sub(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status),
+ status);
+ } else {
+ /* SINTINY */
+ a = floatx80_move(a, status);
+ }
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ sincont:
+ if ((n + adjn) & 1) {
+ /* COSPOLY */
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
+ status); /* B8 */
+ fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
+ status); /* B7 */
+
+ xSign = extractFloatx80Sign(fp0); /* X IS S */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ if (((n + adjn) >> 1) & 1) {
+ xSign ^= 1;
+ posneg1 = make_float32(0xBF800000); /* -1 */
+ } else {
+ xSign ^= 0;
+ posneg1 = make_float32(0x3F800000); /* 1 */
+ } /* X IS NOW R'= SGN*R */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3E21EED90612C972), status),
+ status); /* B6+TB8 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE927E4FB79D9FCF), status),
+ status); /* B5+TB7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A01A01D423), status),
+ status); /* B4+T(B6+TB8) */
+ fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438));
+ fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
+ fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E));
+ fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0xBF000000), status),
+ status); /* B1+T(B3+T(B5+TB7)) */
+ fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
+ fp0 = floatx80_add(fp0, fp1, status);
+ /* [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ /* SINPOLY */
+ xSign = extractFloatx80Sign(fp0); /* X IS R */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */
+
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
+ status); /* A7 */
+ fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
+ status); /* A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE5AE6452A118AE4), status),
+ status); /* A5+T*A7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5341531), status),
+ status); /* A4+T*A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF2A01A01A018B59), status),
+ status); /* A3+T(A5+TA7) */
+ fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF));
+ fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99));
+ fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status); /* R'*S */
+ fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, x, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Arc tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_atan(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, tbl_index;
+ floatx80 fp0, fp1, fp2, fp3, xsave;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ a = packFloatx80(aSign, piby2_exp, pi_sig);
+ float_raise(float_flag_inexact, status);
+ return floatx80_move(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ if (compact < 0x3FFB8000 || compact > 0x4002FFFF) {
+ /* |X| >= 16 or |X| < 1/16 */
+ if (compact > 0x3FFF8000) { /* |X| >= 16 */
+ if (compact > 0x40638000) { /* |X| > 2^(100) */
+ fp0 = packFloatx80(aSign, piby2_exp, pi_sig);
+ fp1 = packFloatx80(aSign, 0x0001, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_sub(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = a;
+ fp1 = packFloatx80(1, one_exp, one_sig); /* -1 */
+ fp1 = floatx80_div(fp1, fp0, status); /* X' = -1/X */
+ xsave = fp1;
+ fp0 = floatx80_mul(fp1, fp1, status); /* Y = X'*X' */
+ fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
+ fp3 = float64_to_floatx80(make_float64(0xBFB70BF398539E6A),
+ status); /* C5 */
+ fp2 = float64_to_floatx80(make_float64(0x3FBC7187962D1D7D),
+ status); /* C4 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* Z*C5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*C4 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBFC24924827107B8), status),
+ status); /* C3+Z*C5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC999999996263E), status),
+ status); /* C2+Z*C4 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* Z*(C3+Z*C5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* Y*(C2+Z*C4) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0xBFD5555555555536), status),
+ status); /* C1+Z*(C3+Z*C5) */
+ fp0 = floatx80_mul(fp0, xsave, status); /* X'*Y */
+ /* [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? */
+ fp0 = floatx80_mul(fp0, fp1, status);
+ fp0 = floatx80_add(fp0, xsave, status);
+ fp1 = packFloatx80(aSign, piby2_exp, pi_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* |X| < 1/16 */
+ if (compact < 0x3FD78000) { /* |X| < 2^(-40) */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = a;
+ xsave = a;
+ fp0 = floatx80_mul(fp0, fp0, status); /* Y = X*X */
+ fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
+ fp2 = float64_to_floatx80(make_float64(0x3FB344447F876989),
+ status); /* B6 */
+ fp3 = float64_to_floatx80(make_float64(0xBFB744EE7FAF45DB),
+ status); /* B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*B6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* Z*B5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FBC71C646940220), status),
+ status); /* B4+Z*B6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBFC24924921872F9),
+ status), status); /* B3+Z*B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*(B4+Z*B6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* Z*(B3+Z*B5) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC9999999998FA9), status),
+ status); /* B2+Z*(B4+Z*B6) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0xBFD5555555555555), status),
+ status); /* B1+Z*(B3+Z*B5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* Y*(B2+Z*(B4+Z*B6)) */
+ fp0 = floatx80_mul(fp0, xsave, status); /* X*Y */
+ /* [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) */
+ fp0 = floatx80_mul(fp0, fp1, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, xsave, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+ } else {
+ aSig &= LIT64(0xF800000000000000);
+ aSig |= LIT64(0x0400000000000000);
+ xsave = packFloatx80(aSign, aExp, aSig); /* F */
+ fp0 = a;
+ fp1 = a; /* X */
+ fp2 = packFloatx80(0, one_exp, one_sig); /* 1 */
+ fp1 = floatx80_mul(fp1, xsave, status); /* X*F */
+ fp0 = floatx80_sub(fp0, xsave, status); /* X-F */
+ fp1 = floatx80_add(fp1, fp2, status); /* 1 + X*F */
+ fp0 = floatx80_div(fp0, fp1, status); /* U = (X-F)/(1+X*F) */
+
+ tbl_index = compact;
+
+ tbl_index &= 0x7FFF0000;
+ tbl_index -= 0x3FFB0000;
+ tbl_index >>= 1;
+ tbl_index += compact & 0x00007800;
+ tbl_index >>= 11;
+
+ fp3 = atan_tbl[tbl_index];
+
+ fp3.high |= aSign ? 0x8000 : 0; /* ATAN(F) */
+
+ fp1 = floatx80_mul(fp0, fp0, status); /* V = U*U */
+ fp2 = float64_to_floatx80(make_float64(0xBFF6687E314987D8),
+ status); /* A3 */
+ fp2 = floatx80_add(fp2, fp1, status); /* A3+V */
+ fp2 = floatx80_mul(fp2, fp1, status); /* V*(A3+V) */
+ fp1 = floatx80_mul(fp1, fp0, status); /* U*V */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x4002AC6934A26DB3), status),
+ status); /* A2+V*(A3+V) */
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0xBFC2476F4E1DA28E), status),
+ status); /* A1+U*V */
+ fp1 = floatx80_mul(fp1, fp2, status); /* A1*U*V*(A2+V*(A3+V)) */
+ fp0 = floatx80_add(fp0, fp1, status); /* ATAN(U) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp3, status); /* ATAN(X) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Arc sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_asin(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ float_raise(float_flag_inexact, status);
+ a = packFloatx80(aSign, piby2_exp, pi_sig);
+ return floatx80_move(a, status);
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp0 = a;
+
+ fp1 = floatx80_sub(one, fp0, status); /* 1 - X */
+ fp2 = floatx80_add(one, fp0, status); /* 1 + X */
+ fp1 = floatx80_mul(fp2, fp1, status); /* (1+X)*(1-X) */
+ fp1 = floatx80_sqrt(fp1, status); /* SQRT((1+X)*(1-X)) */
+ fp0 = floatx80_div(fp0, fp1, status); /* X/SQRT((1+X)*(1-X)) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_atan(fp0, status); /* ATAN(X/SQRT((1+X)*(1-X))) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Arc cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_acos(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aExp == 0 && aSig == 0) {
+ float_raise(float_flag_inexact, status);
+ return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
+ piby2_exp, pi_sig, 0, status);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ if (aSign) { /* X == -1 */
+ a = packFloatx80(0, pi_exp, pi_sig);
+ float_raise(float_flag_inexact, status);
+ return floatx80_move(a, status);
+ } else { /* X == +1 */
+ return packFloatx80(0, 0, 0);
+ }
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp0 = a;
+
+ fp1 = floatx80_add(one, fp0, status); /* 1 + X */
+ fp0 = floatx80_sub(one, fp0, status); /* 1 - X */
+ fp0 = floatx80_div(fp0, fp1, status); /* (1-X)/(1+X) */
+ fp0 = floatx80_sqrt(fp0, status); /* SQRT((1-X)/(1+X)) */
+ fp0 = floatx80_atan(fp0, status); /* ATAN(SQRT((1-X)/(1+X))) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp0, status); /* 2 * ATAN(SQRT((1-X)/(1+X))) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic arc tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_atanh(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ float_raise(float_flag_divbyzero, status);
+ return packFloatx80(aSign, floatx80_infinity.high,
+ floatx80_infinity.low);
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp2 = packFloatx80(aSign, 0x3FFE, one_sig); /* SIGN(X) * (1/2) */
+ fp0 = packFloatx80(0, aExp, aSig); /* Y = |X| */
+ fp1 = packFloatx80(1, aExp, aSig); /* -Y */
+ fp0 = floatx80_add(fp0, fp0, status); /* 2Y */
+ fp1 = floatx80_add(fp1, one, status); /* 1-Y */
+ fp0 = floatx80_div(fp0, fp1, status); /* Z = 2Y/(1-Y) */
+ fp0 = floatx80_lognp1(fp0, status); /* LOG1P(Z) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp2,
+ status); /* ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | e to x minus 1
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_etoxm1(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, n, j, m, m1;
+ floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ return packFloatx80(aSign, one_exp, one_sig);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ if (aExp >= 0x3FFD) { /* |X| >= 1/4 */
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact <= 0x4004C215) { /* |X| <= 70 log2 */
+ fp0 = a;
+ fp1 = a;
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x42B8AA3B), status),
+ status); /* 64/log2 * X */
+ n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+ fp0 = int32_to_floatx80(n, status);
+
+ j = n & 0x3F; /* J = N mod 64 */
+ m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m--;
+ }
+ m1 = -m;
+ /*m += 0x3FFF; // biased exponent of 2^(M) */
+ /*m1 += 0x3FFF; // biased exponent of -2^(-M) */
+
+ fp2 = fp0; /* N */
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0xBC317218), status),
+ status); /* N * L1, L1 = lead(-log2/64) */
+ l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6));
+ fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
+ fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
+ fp0 = floatx80_add(fp0, fp2, status); /* R */
+
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp2 = float32_to_floatx80(make_float32(0x3950097B),
+ status); /* A6 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A6 */
+ fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3AB60B6A),
+ status), fp1, status); /* fp3 is S*A5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F81111111174385), status),
+ status); /* fp2 IS A4+S*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3FA5555555554F5A), status),
+ status); /* fp3 is A3+S*A5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 IS S*(A4+S*A6) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* fp3 IS S*(A3+S*A5) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC5555555555555), status),
+ status); /* fp2 IS A2+S*(A4+S*A6) */
+ fp3 = floatx80_add(fp3, float32_to_floatx80(
+ make_float32(0x3F000000), status),
+ status); /* fp3 IS A1+S*(A3+S*A5) */
+ fp2 = floatx80_mul(fp2, fp1,
+ status); /* fp2 IS S*(A2+S*(A4+S*A6)) */
+ fp1 = floatx80_mul(fp1, fp3,
+ status); /* fp1 IS S*(A1+S*(A3+S*A5)) */
+ fp2 = floatx80_mul(fp2, fp0,
+ status); /* fp2 IS R*S*(A2+S*(A4+S*A6)) */
+ fp0 = floatx80_add(fp0, fp1,
+ status); /* fp0 IS R+S*(A1+S*(A3+S*A5)) */
+ fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
+
+ fp0 = floatx80_mul(fp0, exp_tbl[j],
+ status); /* 2^(J/64)*(Exp(R)-1) */
+
+ if (m >= 64) {
+ fp1 = float32_to_floatx80(exp_tbl2[j], status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp1 = floatx80_add(fp1, onebysc, status);
+ fp0 = floatx80_add(fp0, fp1, status);
+ fp0 = floatx80_add(fp0, exp_tbl[j], status);
+ } else if (m < -3) {
+ fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
+ status), status);
+ fp0 = floatx80_add(fp0, exp_tbl[j], status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp0 = floatx80_add(fp0, onebysc, status);
+ } else { /* -3 <= m <= 63 */
+ fp1 = exp_tbl[j];
+ fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
+ status), status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp1 = floatx80_add(fp1, onebysc, status);
+ fp0 = floatx80_add(fp0, fp1, status);
+ }
+
+ sc = packFloatx80(0, m + 0x3FFF, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, sc, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X| > 70 log2 */
+ if (aSign) {
+ fp0 = float32_to_floatx80(make_float32(0xBF800000),
+ status); /* -1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status),
+ status); /* -1 + 2^(-126) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ return floatx80_etox(a, status);
+ }
+ }
+ } else { /* |X| < 1/4 */
+ if (aExp >= 0x3FBE) {
+ fp0 = a;
+ fp0 = floatx80_mul(fp0, fp0, status); /* S = X*X */
+ fp1 = float32_to_floatx80(make_float32(0x2F30CAA8),
+ status); /* B12 */
+ fp1 = floatx80_mul(fp1, fp0, status); /* S * B12 */
+ fp2 = float32_to_floatx80(make_float32(0x310F8290),
+ status); /* B11 */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0x32D73220), status),
+ status); /* B10 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(
+ make_float32(0x3493F281), status),
+ status); /* B9 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5774682), status),
+ status); /* B8 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A019D7CB68), status),
+ status); /* B7 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3F2A01A01A019DF3), status),
+ status); /* B6 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F56C16C16C170E2), status),
+ status); /* B5 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3F81111111111111), status),
+ status); /* B4 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FA5555555555555), status),
+ status); /* B3 */
+ fp3 = packFloatx80(0, 0x3FFC, LIT64(0xAAAAAAAAAAAAAAAB));
+ fp1 = floatx80_add(fp1, fp3, status); /* B2 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, a, status);
+
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x3F000000), status),
+ status); /* S*B1 */
+ fp1 = floatx80_add(fp1, fp2, status); /* Q */
+ fp0 = floatx80_add(fp0, fp1, status); /* S*B1+Q */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X| < 2^(-65) */
+ sc = packFloatx80(1, 1, one_sig);
+ fp0 = a;
+
+ if (aExp < 0x0033) { /* |X| < 2^(-16382) */
+ fp0 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x48B0000000000000), status),
+ status);
+ fp0 = floatx80_add(fp0, sc, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3730000000000000), status),
+ status);
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, sc, status);
+ }
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tanh(floatx80 a, float_status *status)
+{
+ flag aSign, vSign;
+ int32_t aExp, vExp;
+ uint64_t aSig, vSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1;
+ uint32_t sign;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(aSign, one_exp, one_sig);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) {
+ /* TANHBORS */
+ if (compact < 0x3FFF8000) {
+ /* TANHSM */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ if (compact > 0x40048AA1) {
+ /* TANHHUGE */
+ sign = 0x3F800000;
+ sign |= aSign ? 0x80000000 : 0x00000000;
+ fp0 = float32_to_floatx80(make_float32(sign), status);
+ sign &= 0x80000000;
+ sign ^= 0x80800000; /* -SIGN(X)*EPS */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(make_float32(sign),
+ status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
+ fp0 = floatx80_etox(fp0, status); /* FP0 IS EXP(Y) */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000),
+ status), status); /* EXP(Y)+1 */
+ sign = aSign ? 0x80000000 : 0x00000000;
+ fp1 = floatx80_div(float32_to_floatx80(make_float32(
+ sign ^ 0xC0000000), status), fp0,
+ status); /* -SIGN(X)*2 / [EXP(Y)+1] */
+ fp0 = float32_to_floatx80(make_float32(sign | 0x3F800000),
+ status); /* SIGN */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp1, fp0, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+ } else { /* 2**(-40) < |X| < (5/2)LOG2 */
+ fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
+ fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
+ fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x40000000),
+ status),
+ status); /* Z+2 */
+
+ vSign = extractFloatx80Sign(fp1);
+ vExp = extractFloatx80Exp(fp1);
+ vSig = extractFloatx80Frac(fp1);
+
+ fp1 = packFloatx80(vSign ^ aSign, vExp, vSig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sinh(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2;
+ float32 fact;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(aSign, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact > 0x400CB167) {
+ /* SINHBIG */
+ if (compact > 0x400CB2B3) {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ aSign, 0x8000, aSig, 0, status);
+ } else {
+ fp0 = floatx80_abs(a); /* Y = |X| */
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x40C62D38D3D64634), status),
+ status); /* (|X|-16381LOG2_LEAD) */
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x3D6F90AEB1E75CC7), status),
+ status); /* |X| - 16381 LOG2, ACCURATE */
+ fp0 = floatx80_etox(fp0, status);
+ fp2 = packFloatx80(aSign, 0x7FFB, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp2, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* |X| < 16380 LOG2 */
+ fp0 = floatx80_abs(a); /* Y = |X| */
+ fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
+ fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* 1+Z */
+ fp2 = fp0;
+ fp0 = floatx80_div(fp0, fp1, status); /* Z/(1+Z) */
+ fp0 = floatx80_add(fp0, fp2, status);
+
+ fact = packFloat32(aSign, 0x7E, 0);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_cosh(floatx80 a, float_status *status)
+{
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact > 0x400CB167) {
+ if (compact > 0x400CB2B3) {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+ return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
+ 0x8000, one_sig, 0, status);
+ } else {
+ fp0 = packFloatx80(0, aExp, aSig);
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x40C62D38D3D64634), status),
+ status);
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x3D6F90AEB1E75CC7), status),
+ status);
+ fp0 = floatx80_etox(fp0, status);
+ fp1 = packFloatx80(0, 0x7FFB, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+
+ fp0 = packFloatx80(0, aExp, aSig); /* |X| */
+ fp0 = floatx80_etox(fp0, status); /* EXP(|X|) */
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(make_float32(0x3F000000),
+ status), status); /* (1/2)*EXP(|X|) */
+ fp1 = float32_to_floatx80(make_float32(0x3E800000), status); /* 1/4 */
+ fp1 = floatx80_div(fp1, fp0, status); /* 1/(2*EXP(|X|)) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h
index d28e49fe9f..602661d5a8 100644
--- a/target/m68k/softfloat.h
+++ b/target/m68k/softfloat.h
@@ -34,4 +34,15 @@ floatx80 floatx80_log2(floatx80 a, float_status *status);
floatx80 floatx80_etox(floatx80 a, float_status *status);
floatx80 floatx80_twotox(floatx80 a, float_status *status);
floatx80 floatx80_tentox(floatx80 a, float_status *status);
+floatx80 floatx80_tan(floatx80 a, float_status *status);
+floatx80 floatx80_sin(floatx80 a, float_status *status);
+floatx80 floatx80_cos(floatx80 a, float_status *status);
+floatx80 floatx80_atan(floatx80 a, float_status *status);
+floatx80 floatx80_asin(floatx80 a, float_status *status);
+floatx80 floatx80_acos(floatx80 a, float_status *status);
+floatx80 floatx80_atanh(floatx80 a, float_status *status);
+floatx80 floatx80_etoxm1(floatx80 a, float_status *status);
+floatx80 floatx80_tanh(floatx80 a, float_status *status);
+floatx80 floatx80_sinh(floatx80 a, float_status *status);
+floatx80 floatx80_cosh(floatx80 a, float_status *status);
#endif
diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h
index dd76dc0373..3f1419ee6e 100644
--- a/target/m68k/softfloat_fpsp_tables.h
+++ b/target/m68k/softfloat_fpsp_tables.h
@@ -371,4 +371,271 @@ static const uint32_t exp2_tbl2[64] = {
0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F,
0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581
};
+
+static const floatx80 pi_tbl[65] = {
+ make_floatx80_init(0xC004, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC004, 0xC2C75BCD105D7C23),
+ make_floatx80_init(0xC004, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0xC004, 0xB6365E22EE46F000),
+ make_floatx80_init(0xC004, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC004, 0xA9A56078CC3063DD),
+ make_floatx80_init(0xC004, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0xC004, 0x9D1462CEAA19D7B9),
+ make_floatx80_init(0xC004, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC004, 0x9083652488034B96),
+ make_floatx80_init(0xC004, 0x8A3AE64F76F80584),
+ make_floatx80_init(0xC004, 0x83F2677A65ECBF73),
+ make_floatx80_init(0xC003, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC003, 0xEEC2D3A087AC669F),
+ make_floatx80_init(0xC003, 0xE231D5F66595DA7B),
+ make_floatx80_init(0xC003, 0xD5A0D84C437F4E58),
+ make_floatx80_init(0xC003, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC003, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0xC003, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC003, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0xC003, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC003, 0x8A3AE64F76F80584),
+ make_floatx80_init(0xC002, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC002, 0xE231D5F66595DA7B),
+ make_floatx80_init(0xC002, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC002, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC002, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC001, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC001, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC001, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC000, 0xC90FDAA22168C235),
+ make_floatx80_init(0xBFFF, 0xC90FDAA22168C235),
+ make_floatx80_init(0x0000, 0x0000000000000000),
+ make_floatx80_init(0x3FFF, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4000, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4001, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4001, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4001, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4002, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4002, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4002, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4002, 0xE231D5F66595DA7B),
+ make_floatx80_init(0x4002, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4003, 0x8A3AE64F76F80584),
+ make_floatx80_init(0x4003, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4003, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0x4003, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4003, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0x4003, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4003, 0xD5A0D84C437F4E58),
+ make_floatx80_init(0x4003, 0xE231D5F66595DA7B),
+ make_floatx80_init(0x4003, 0xEEC2D3A087AC669F),
+ make_floatx80_init(0x4003, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4004, 0x83F2677A65ECBF73),
+ make_floatx80_init(0x4004, 0x8A3AE64F76F80584),
+ make_floatx80_init(0x4004, 0x9083652488034B96),
+ make_floatx80_init(0x4004, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4004, 0x9D1462CEAA19D7B9),
+ make_floatx80_init(0x4004, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0x4004, 0xA9A56078CC3063DD),
+ make_floatx80_init(0x4004, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4004, 0xB6365E22EE46F000),
+ make_floatx80_init(0x4004, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0x4004, 0xC2C75BCD105D7C23),
+ make_floatx80_init(0x4004, 0xC90FDAA22168C235)
+};
+
+static const float32 pi_tbl2[65] = {
+ const_float32(0x21800000),
+ const_float32(0xA0D00000),
+ const_float32(0xA1E80000),
+ const_float32(0x21480000),
+ const_float32(0xA1200000),
+ const_float32(0x21FC0000),
+ const_float32(0x21100000),
+ const_float32(0xA1580000),
+ const_float32(0x21E00000),
+ const_float32(0x20B00000),
+ const_float32(0xA1880000),
+ const_float32(0x21C40000),
+ const_float32(0x20000000),
+ const_float32(0x21380000),
+ const_float32(0xA1300000),
+ const_float32(0x9FC00000),
+ const_float32(0x21000000),
+ const_float32(0xA1680000),
+ const_float32(0xA0A00000),
+ const_float32(0x20900000),
+ const_float32(0x21600000),
+ const_float32(0xA1080000),
+ const_float32(0x1F800000),
+ const_float32(0xA0B00000),
+ const_float32(0x20800000),
+ const_float32(0xA0200000),
+ const_float32(0x20E00000),
+ const_float32(0x1F000000),
+ const_float32(0x20000000),
+ const_float32(0x20600000),
+ const_float32(0x1F800000),
+ const_float32(0x1F000000),
+ const_float32(0x00000000),
+ const_float32(0x9F000000),
+ const_float32(0x9F800000),
+ const_float32(0xA0600000),
+ const_float32(0xA0000000),
+ const_float32(0x9F000000),
+ const_float32(0xA0E00000),
+ const_float32(0x20200000),
+ const_float32(0xA0800000),
+ const_float32(0x20B00000),
+ const_float32(0x9F800000),
+ const_float32(0x21080000),
+ const_float32(0xA1600000),
+ const_float32(0xA0900000),
+ const_float32(0x20A00000),
+ const_float32(0x21680000),
+ const_float32(0xA1000000),
+ const_float32(0x1FC00000),
+ const_float32(0x21300000),
+ const_float32(0xA1380000),
+ const_float32(0xA0000000),
+ const_float32(0xA1C40000),
+ const_float32(0x21880000),
+ const_float32(0xA0B00000),
+ const_float32(0xA1E00000),
+ const_float32(0x21580000),
+ const_float32(0xA1100000),
+ const_float32(0xA1FC0000),
+ const_float32(0x21200000),
+ const_float32(0xA1480000),
+ const_float32(0x21E80000),
+ const_float32(0x20D00000),
+ const_float32(0xA1800000),
+};
+
+static const floatx80 atan_tbl[128] = {
+ make_floatx80_init(0x3FFB, 0x83D152C5060B7A51),
+ make_floatx80_init(0x3FFB, 0x8BC8544565498B8B),
+ make_floatx80_init(0x3FFB, 0x93BE406017626B0D),
+ make_floatx80_init(0x3FFB, 0x9BB3078D35AEC202),
+ make_floatx80_init(0x3FFB, 0xA3A69A525DDCE7DE),
+ make_floatx80_init(0x3FFB, 0xAB98E94362765619),
+ make_floatx80_init(0x3FFB, 0xB389E502F9C59862),
+ make_floatx80_init(0x3FFB, 0xBB797E436B09E6FB),
+ make_floatx80_init(0x3FFB, 0xC367A5C739E5F446),
+ make_floatx80_init(0x3FFB, 0xCB544C61CFF7D5C6),
+ make_floatx80_init(0x3FFB, 0xD33F62F82488533E),
+ make_floatx80_init(0x3FFB, 0xDB28DA8162404C77),
+ make_floatx80_init(0x3FFB, 0xE310A4078AD34F18),
+ make_floatx80_init(0x3FFB, 0xEAF6B0A8188EE1EB),
+ make_floatx80_init(0x3FFB, 0xF2DAF1949DBE79D5),
+ make_floatx80_init(0x3FFB, 0xFABD581361D47E3E),
+ make_floatx80_init(0x3FFC, 0x8346AC210959ECC4),
+ make_floatx80_init(0x3FFC, 0x8B232A08304282D8),
+ make_floatx80_init(0x3FFC, 0x92FB70B8D29AE2F9),
+ make_floatx80_init(0x3FFC, 0x9ACF476F5CCD1CB4),
+ make_floatx80_init(0x3FFC, 0xA29E76304954F23F),
+ make_floatx80_init(0x3FFC, 0xAA68C5D08AB85230),
+ make_floatx80_init(0x3FFC, 0xB22DFFFD9D539F83),
+ make_floatx80_init(0x3FFC, 0xB9EDEF453E900EA5),
+ make_floatx80_init(0x3FFC, 0xC1A85F1CC75E3EA5),
+ make_floatx80_init(0x3FFC, 0xC95D1BE828138DE6),
+ make_floatx80_init(0x3FFC, 0xD10BF300840D2DE4),
+ make_floatx80_init(0x3FFC, 0xD8B4B2BA6BC05E7A),
+ make_floatx80_init(0x3FFC, 0xE0572A6BB42335F6),
+ make_floatx80_init(0x3FFC, 0xE7F32A70EA9CAA8F),
+ make_floatx80_init(0x3FFC, 0xEF88843264ECEFAA),
+ make_floatx80_init(0x3FFC, 0xF7170A28ECC06666),
+ make_floatx80_init(0x3FFD, 0x812FD288332DAD32),
+ make_floatx80_init(0x3FFD, 0x88A8D1B1218E4D64),
+ make_floatx80_init(0x3FFD, 0x9012AB3F23E4AEE8),
+ make_floatx80_init(0x3FFD, 0x976CC3D411E7F1B9),
+ make_floatx80_init(0x3FFD, 0x9EB689493889A227),
+ make_floatx80_init(0x3FFD, 0xA5EF72C34487361B),
+ make_floatx80_init(0x3FFD, 0xAD1700BAF07A7227),
+ make_floatx80_init(0x3FFD, 0xB42CBCFAFD37EFB7),
+ make_floatx80_init(0x3FFD, 0xBB303A940BA80F89),
+ make_floatx80_init(0x3FFD, 0xC22115C6FCAEBBAF),
+ make_floatx80_init(0x3FFD, 0xC8FEF3E686331221),
+ make_floatx80_init(0x3FFD, 0xCFC98330B4000C70),
+ make_floatx80_init(0x3FFD, 0xD6807AA1102C5BF9),
+ make_floatx80_init(0x3FFD, 0xDD2399BC31252AA3),
+ make_floatx80_init(0x3FFD, 0xE3B2A8556B8FC517),
+ make_floatx80_init(0x3FFD, 0xEA2D764F64315989),
+ make_floatx80_init(0x3FFD, 0xF3BF5BF8BAD1A21D),
+ make_floatx80_init(0x3FFE, 0x801CE39E0D205C9A),
+ make_floatx80_init(0x3FFE, 0x8630A2DADA1ED066),
+ make_floatx80_init(0x3FFE, 0x8C1AD445F3E09B8C),
+ make_floatx80_init(0x3FFE, 0x91DB8F1664F350E2),
+ make_floatx80_init(0x3FFE, 0x97731420365E538C),
+ make_floatx80_init(0x3FFE, 0x9CE1C8E6A0B8CDBA),
+ make_floatx80_init(0x3FFE, 0xA22832DBCADAAE09),
+ make_floatx80_init(0x3FFE, 0xA746F2DDB7602294),
+ make_floatx80_init(0x3FFE, 0xAC3EC0FB997DD6A2),
+ make_floatx80_init(0x3FFE, 0xB110688AEBDC6F6A),
+ make_floatx80_init(0x3FFE, 0xB5BCC49059ECC4B0),
+ make_floatx80_init(0x3FFE, 0xBA44BC7DD470782F),
+ make_floatx80_init(0x3FFE, 0xBEA94144FD049AAC),
+ make_floatx80_init(0x3FFE, 0xC2EB4ABB661628B6),
+ make_floatx80_init(0x3FFE, 0xC70BD54CE602EE14),
+ make_floatx80_init(0x3FFE, 0xCD000549ADEC7159),
+ make_floatx80_init(0x3FFE, 0xD48457D2D8EA4EA3),
+ make_floatx80_init(0x3FFE, 0xDB948DA712DECE3B),
+ make_floatx80_init(0x3FFE, 0xE23855F969E8096A),
+ make_floatx80_init(0x3FFE, 0xE8771129C4353259),
+ make_floatx80_init(0x3FFE, 0xEE57C16E0D379C0D),
+ make_floatx80_init(0x3FFE, 0xF3E10211A87C3779),
+ make_floatx80_init(0x3FFE, 0xF919039D758B8D41),
+ make_floatx80_init(0x3FFE, 0xFE058B8F64935FB3),
+ make_floatx80_init(0x3FFF, 0x8155FB497B685D04),
+ make_floatx80_init(0x3FFF, 0x83889E3549D108E1),
+ make_floatx80_init(0x3FFF, 0x859CFA76511D724B),
+ make_floatx80_init(0x3FFF, 0x87952ECFFF8131E7),
+ make_floatx80_init(0x3FFF, 0x89732FD19557641B),
+ make_floatx80_init(0x3FFF, 0x8B38CAD101932A35),
+ make_floatx80_init(0x3FFF, 0x8CE7A8D8301EE6B5),
+ make_floatx80_init(0x3FFF, 0x8F46A39E2EAE5281),
+ make_floatx80_init(0x3FFF, 0x922DA7D791888487),
+ make_floatx80_init(0x3FFF, 0x94D19FCBDEDF5241),
+ make_floatx80_init(0x3FFF, 0x973AB94419D2A08B),
+ make_floatx80_init(0x3FFF, 0x996FF00E08E10B96),
+ make_floatx80_init(0x3FFF, 0x9B773F9512321DA7),
+ make_floatx80_init(0x3FFF, 0x9D55CC320F935624),
+ make_floatx80_init(0x3FFF, 0x9F100575006CC571),
+ make_floatx80_init(0x3FFF, 0xA0A9C290D97CC06C),
+ make_floatx80_init(0x3FFF, 0xA22659EBEBC0630A),
+ make_floatx80_init(0x3FFF, 0xA388B4AFF6EF0EC9),
+ make_floatx80_init(0x3FFF, 0xA4D35F1061D292C4),
+ make_floatx80_init(0x3FFF, 0xA60895DCFBE3187E),
+ make_floatx80_init(0x3FFF, 0xA72A51DC7367BEAC),
+ make_floatx80_init(0x3FFF, 0xA83A51530956168F),
+ make_floatx80_init(0x3FFF, 0xA93A20077539546E),
+ make_floatx80_init(0x3FFF, 0xAA9E7245023B2605),
+ make_floatx80_init(0x3FFF, 0xAC4C84BA6FE4D58F),
+ make_floatx80_init(0x3FFF, 0xADCE4A4A606B9712),
+ make_floatx80_init(0x3FFF, 0xAF2A2DCD8D263C9C),
+ make_floatx80_init(0x3FFF, 0xB0656F81F22265C7),
+ make_floatx80_init(0x3FFF, 0xB18465150F71496A),
+ make_floatx80_init(0x3FFF, 0xB28AAA156F9ADA35),
+ make_floatx80_init(0x3FFF, 0xB37B44FF3766B895),
+ make_floatx80_init(0x3FFF, 0xB458C3DCE9630433),
+ make_floatx80_init(0x3FFF, 0xB525529D562246BD),
+ make_floatx80_init(0x3FFF, 0xB5E2CCA95F9D88CC),
+ make_floatx80_init(0x3FFF, 0xB692CADA7ACA1ADA),
+ make_floatx80_init(0x3FFF, 0xB736AEA7A6925838),
+ make_floatx80_init(0x3FFF, 0xB7CFAB287E9F7B36),
+ make_floatx80_init(0x3FFF, 0xB85ECC66CB219835),
+ make_floatx80_init(0x3FFF, 0xB8E4FD5A20A593DA),
+ make_floatx80_init(0x3FFF, 0xB99F41F64AFF9BB5),
+ make_floatx80_init(0x3FFF, 0xBA7F1E17842BBE7B),
+ make_floatx80_init(0x3FFF, 0xBB4712857637E17D),
+ make_floatx80_init(0x3FFF, 0xBBFABE8A4788DF6F),
+ make_floatx80_init(0x3FFF, 0xBC9D0FAD2B689D79),
+ make_floatx80_init(0x3FFF, 0xBD306A39471ECD86),
+ make_floatx80_init(0x3FFF, 0xBDB6C731856AF18A),
+ make_floatx80_init(0x3FFF, 0xBE31CAC502E80D70),
+ make_floatx80_init(0x3FFF, 0xBEA2D55CE33194E2),
+ make_floatx80_init(0x3FFF, 0xBF0B10B7C03128F0),
+ make_floatx80_init(0x3FFF, 0xBF6B7A18DACB778D),
+ make_floatx80_init(0x3FFF, 0xBFC4EA4663FA18F6),
+ make_floatx80_init(0x3FFF, 0xC0181BDE8B89A454),
+ make_floatx80_init(0x3FFF, 0xC065B066CFBF6439),
+ make_floatx80_init(0x3FFF, 0xC0AE345F56340AE6),
+ make_floatx80_init(0x3FFF, 0xC0F222919CB9E6A7)
+};
#endif
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 6d5bde0777..6beaf9ed66 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -123,8 +123,34 @@ typedef struct DisasContext {
int done_mac;
int writeback_mask;
TCGv writeback[8];
+#define MAX_TO_RELEASE 8
+ int release_count;
+ TCGv release[MAX_TO_RELEASE];
} DisasContext;
+static void init_release_array(DisasContext *s)
+{
+#ifdef CONFIG_DEBUG_TCG
+ memset(s->release, 0, sizeof(s->release));
+#endif
+ s->release_count = 0;
+}
+
+static void do_release(DisasContext *s)
+{
+ int i;
+ for (i = 0; i < s->release_count; i++) {
+ tcg_temp_free(s->release[i]);
+ }
+ init_release_array(s);
+}
+
+static TCGv mark_to_release(DisasContext *s, TCGv tmp)
+{
+ g_assert(s->release_count < MAX_TO_RELEASE);
+ return s->release[s->release_count++] = tmp;
+}
+
static TCGv get_areg(DisasContext *s, unsigned regno)
{
if (s->writeback_mask & (1 << regno)) {
@@ -347,7 +373,8 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val,
gen_store(s, opsize, addr, val, index);
return store_dummy;
} else {
- return gen_load(s, opsize, addr, what == EA_LOADS, index);
+ return mark_to_release(s, gen_load(s, opsize, addr,
+ what == EA_LOADS, index));
}
}
@@ -439,7 +466,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
} else {
bd = 0;
}
- tmp = tcg_temp_new();
+ tmp = mark_to_release(s, tcg_temp_new());
if ((ext & 0x44) == 0) {
/* pre-index */
add = gen_addr_index(s, ext, tmp);
@@ -449,7 +476,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
if ((ext & 0x80) == 0) {
/* base not suppressed */
if (IS_NULL_QREG(base)) {
- base = tcg_const_i32(offset + bd);
+ base = mark_to_release(s, tcg_const_i32(offset + bd));
bd = 0;
}
if (!IS_NULL_QREG(add)) {
@@ -465,11 +492,11 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
add = tmp;
}
} else {
- add = tcg_const_i32(bd);
+ add = mark_to_release(s, tcg_const_i32(bd));
}
if ((ext & 3) != 0) {
/* memory indirect */
- base = gen_load(s, OS_LONG, add, 0, IS_USER(s));
+ base = mark_to_release(s, gen_load(s, OS_LONG, add, 0, IS_USER(s)));
if ((ext & 0x44) == 4) {
add = gen_addr_index(s, ext, tmp);
tcg_gen_add_i32(tmp, add, base);
@@ -494,7 +521,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
}
} else {
/* brief extension word format */
- tmp = tcg_temp_new();
+ tmp = mark_to_release(s, tcg_temp_new());
add = gen_addr_index(s, ext, tmp);
if (!IS_NULL_QREG(base)) {
tcg_gen_add_i32(tmp, add, base);
@@ -617,14 +644,14 @@ static void gen_flush_flags(DisasContext *s)
s->cc_op = CC_OP_FLAGS;
}
-static inline TCGv gen_extend(TCGv val, int opsize, int sign)
+static inline TCGv gen_extend(DisasContext *s, TCGv val, int opsize, int sign)
{
TCGv tmp;
if (opsize == OS_LONG) {
tmp = val;
} else {
- tmp = tcg_temp_new();
+ tmp = mark_to_release(s, tcg_temp_new());
gen_ext(tmp, val, opsize, sign);
}
@@ -746,7 +773,7 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s,
return NULL_QREG;
}
reg = get_areg(s, reg0);
- tmp = tcg_temp_new();
+ tmp = mark_to_release(s, tcg_temp_new());
if (reg0 == 7 && opsize == OS_BYTE &&
m68k_feature(s->env, M68K_FEATURE_M68000)) {
tcg_gen_subi_i32(tmp, reg, 2);
@@ -756,7 +783,7 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s,
return tmp;
case 5: /* Indirect displacement. */
reg = get_areg(s, reg0);
- tmp = tcg_temp_new();
+ tmp = mark_to_release(s, tcg_temp_new());
ext = read_im16(env, s);
tcg_gen_addi_i32(tmp, reg, (int16_t)ext);
return tmp;
@@ -767,14 +794,14 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s,
switch (reg0) {
case 0: /* Absolute short. */
offset = (int16_t)read_im16(env, s);
- return tcg_const_i32(offset);
+ return mark_to_release(s, tcg_const_i32(offset));
case 1: /* Absolute long. */
offset = read_im32(env, s);
- return tcg_const_i32(offset);
+ return mark_to_release(s, tcg_const_i32(offset));
case 2: /* pc displacement */
offset = s->pc;
offset += (int16_t)read_im16(env, s);
- return tcg_const_i32(offset);
+ return mark_to_release(s, tcg_const_i32(offset));
case 3: /* pc index+displacement. */
return gen_lea_indexed(env, s, NULL_QREG);
case 4: /* Immediate. */
@@ -811,7 +838,7 @@ static TCGv gen_ea_mode(CPUM68KState *env, DisasContext *s, int mode, int reg0,
gen_partset_reg(opsize, reg, val);
return store_dummy;
} else {
- return gen_extend(reg, opsize, what == EA_LOADS);
+ return gen_extend(s, reg, opsize, what == EA_LOADS);
}
case 1: /* Address register direct. */
reg = get_areg(s, reg0);
@@ -819,7 +846,7 @@ static TCGv gen_ea_mode(CPUM68KState *env, DisasContext *s, int mode, int reg0,
tcg_gen_mov_i32(reg, val);
return store_dummy;
} else {
- return gen_extend(reg, opsize, what == EA_LOADS);
+ return gen_extend(s, reg, opsize, what == EA_LOADS);
}
case 2: /* Indirect register */
reg = get_areg(s, reg0);
@@ -900,7 +927,7 @@ static TCGv gen_ea_mode(CPUM68KState *env, DisasContext *s, int mode, int reg0,
default:
g_assert_not_reached();
}
- return tcg_const_i32(offset);
+ return mark_to_release(s, tcg_const_i32(offset));
default:
return NULL_QREG;
}
@@ -1759,8 +1786,8 @@ DISAS_INSN(abcd_reg)
gen_flush_flags(s); /* !Z is sticky */
- src = gen_extend(DREG(insn, 0), OS_BYTE, 0);
- dest = gen_extend(DREG(insn, 9), OS_BYTE, 0);
+ src = gen_extend(s, DREG(insn, 0), OS_BYTE, 0);
+ dest = gen_extend(s, DREG(insn, 9), OS_BYTE, 0);
bcd_add(dest, src);
gen_partset_reg(OS_BYTE, DREG(insn, 9), dest);
@@ -1794,8 +1821,8 @@ DISAS_INSN(sbcd_reg)
gen_flush_flags(s); /* !Z is sticky */
- src = gen_extend(DREG(insn, 0), OS_BYTE, 0);
- dest = gen_extend(DREG(insn, 9), OS_BYTE, 0);
+ src = gen_extend(s, DREG(insn, 0), OS_BYTE, 0);
+ dest = gen_extend(s, DREG(insn, 9), OS_BYTE, 0);
bcd_sub(dest, src);
@@ -1856,7 +1883,7 @@ DISAS_INSN(addsub)
add = (insn & 0x4000) != 0;
opsize = insn_opsize(insn);
- reg = gen_extend(DREG(insn, 9), opsize, 1);
+ reg = gen_extend(s, DREG(insn, 9), opsize, 1);
dest = tcg_temp_new();
if (insn & 0x100) {
SRC_EA(env, tmp, opsize, 1, &addr);
@@ -2386,7 +2413,7 @@ DISAS_INSN(cas)
return;
}
- cmp = gen_extend(DREG(ext, 0), opsize, 1);
+ cmp = gen_extend(s, DREG(ext, 0), opsize, 1);
/* if <EA> == Dc then
* <EA> = Du
@@ -3055,7 +3082,7 @@ DISAS_INSN(or)
int opsize;
opsize = insn_opsize(insn);
- reg = gen_extend(DREG(insn, 9), opsize, 0);
+ reg = gen_extend(s, DREG(insn, 9), opsize, 0);
dest = tcg_temp_new();
if (insn & 0x100) {
SRC_EA(env, src, opsize, 0, &addr);
@@ -3120,8 +3147,8 @@ DISAS_INSN(subx_reg)
opsize = insn_opsize(insn);
- src = gen_extend(DREG(insn, 0), opsize, 1);
- dest = gen_extend(DREG(insn, 9), opsize, 1);
+ src = gen_extend(s, DREG(insn, 0), opsize, 1);
+ dest = gen_extend(s, DREG(insn, 9), opsize, 1);
gen_subx(s, src, dest, opsize);
@@ -3176,7 +3203,7 @@ DISAS_INSN(cmp)
opsize = insn_opsize(insn);
SRC_EA(env, src, opsize, 1, NULL);
- reg = gen_extend(DREG(insn, 9), opsize, 1);
+ reg = gen_extend(s, DREG(insn, 9), opsize, 1);
gen_update_cc_cmp(s, reg, src, opsize);
}
@@ -3329,8 +3356,8 @@ DISAS_INSN(addx_reg)
opsize = insn_opsize(insn);
- dest = gen_extend(DREG(insn, 9), opsize, 1);
- src = gen_extend(DREG(insn, 0), opsize, 1);
+ dest = gen_extend(s, DREG(insn, 9), opsize, 1);
+ src = gen_extend(s, DREG(insn, 0), opsize, 1);
gen_addx(s, src, dest, opsize);
@@ -3369,7 +3396,7 @@ static inline void shift_im(DisasContext *s, uint16_t insn, int opsize)
int logical = insn & 8;
int left = insn & 0x100;
int bits = opsize_bytes(opsize) * 8;
- TCGv reg = gen_extend(DREG(insn, 0), opsize, !logical);
+ TCGv reg = gen_extend(s, DREG(insn, 0), opsize, !logical);
if (count == 0) {
count = 8;
@@ -3419,7 +3446,7 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize)
int logical = insn & 8;
int left = insn & 0x100;
int bits = opsize_bytes(opsize) * 8;
- TCGv reg = gen_extend(DREG(insn, 0), opsize, !logical);
+ TCGv reg = gen_extend(s, DREG(insn, 0), opsize, !logical);
TCGv s32;
TCGv_i64 t64, s64;
@@ -3556,7 +3583,7 @@ DISAS_INSN(shift_mem)
while M68000 sets if the most significant bit is changed at
any time during the shift operation */
if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) {
- src = gen_extend(src, OS_WORD, 1);
+ src = gen_extend(s, src, OS_WORD, 1);
tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, src);
}
} else {
@@ -3789,7 +3816,7 @@ DISAS_INSN(rotate8_im)
TCGv shift;
int tmp;
- reg = gen_extend(DREG(insn, 0), OS_BYTE, 0);
+ reg = gen_extend(s, DREG(insn, 0), OS_BYTE, 0);
tmp = (insn >> 9) & 7;
if (tmp == 0) {
@@ -3816,7 +3843,7 @@ DISAS_INSN(rotate16_im)
TCGv shift;
int tmp;
- reg = gen_extend(DREG(insn, 0), OS_WORD, 0);
+ reg = gen_extend(s, DREG(insn, 0), OS_WORD, 0);
tmp = (insn >> 9) & 7;
if (tmp == 0) {
tmp = 8;
@@ -3876,7 +3903,7 @@ DISAS_INSN(rotate8_reg)
TCGv t0, t1;
int left = (insn & 0x100);
- reg = gen_extend(DREG(insn, 0), OS_BYTE, 0);
+ reg = gen_extend(s, DREG(insn, 0), OS_BYTE, 0);
src = DREG(insn, 9);
/* shift in [0..63] */
t0 = tcg_temp_new_i32();
@@ -3911,7 +3938,7 @@ DISAS_INSN(rotate16_reg)
TCGv t0, t1;
int left = (insn & 0x100);
- reg = gen_extend(DREG(insn, 0), OS_WORD, 0);
+ reg = gen_extend(s, DREG(insn, 0), OS_WORD, 0);
src = DREG(insn, 9);
/* shift in [0..63] */
t0 = tcg_temp_new_i32();
@@ -4353,7 +4380,7 @@ DISAS_INSN(chk)
return;
}
SRC_EA(env, src, opsize, 1, NULL);
- reg = gen_extend(DREG(insn, 9), opsize, 1);
+ reg = gen_extend(s, DREG(insn, 9), opsize, 1);
gen_flush_flags(s);
gen_helper_chk(cpu_env, reg, src);
@@ -5042,6 +5069,9 @@ DISAS_INSN(fpu)
case 1: /* fint */
gen_helper_firound(cpu_env, cpu_dest, cpu_src);
break;
+ case 2: /* fsinh */
+ gen_helper_fsinh(cpu_env, cpu_dest, cpu_src);
+ break;
case 3: /* fintrz */
gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src);
break;
@@ -5057,6 +5087,24 @@ DISAS_INSN(fpu)
case 0x06: /* flognp1 */
gen_helper_flognp1(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x09: /* ftanh */
+ gen_helper_ftanh(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0a: /* fatan */
+ gen_helper_fatan(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0c: /* fasin */
+ gen_helper_fasin(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0d: /* fatanh */
+ gen_helper_fatanh(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0e: /* fsin */
+ gen_helper_fsin(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0f: /* ftan */
+ gen_helper_ftan(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x10: /* fetox */
gen_helper_fetox(cpu_env, cpu_dest, cpu_src);
break;
@@ -5084,6 +5132,9 @@ DISAS_INSN(fpu)
case 0x5c: /* fdabs */
gen_helper_fdabs(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x19: /* fcosh */
+ gen_helper_fcosh(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x1a: /* fneg */
gen_helper_fneg(cpu_env, cpu_dest, cpu_src);
break;
@@ -5093,6 +5144,12 @@ DISAS_INSN(fpu)
case 0x5e: /* fdneg */
gen_helper_fdneg(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x1c: /* facos */
+ gen_helper_facos(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x1d: /* fcos */
+ gen_helper_fcos(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x1e: /* fgetexp */
gen_helper_fgetexp(cpu_env, cpu_dest, cpu_src);
break;
@@ -5150,6 +5207,14 @@ DISAS_INSN(fpu)
case 0x6c: /* fdsub */
gen_helper_fdsub(cpu_env, cpu_dest, cpu_src, cpu_dest);
break;
+ case 0x30: case 0x31: case 0x32:
+ case 0x33: case 0x34: case 0x35:
+ case 0x36: case 0x37: {
+ TCGv_ptr cpu_dest2 = gen_fp_ptr(REG(ext, 0));
+ gen_helper_fsincos(cpu_env, cpu_dest, cpu_dest2, cpu_src);
+ tcg_temp_free_ptr(cpu_dest2);
+ }
+ break;
case 0x38: /* fcmp */
gen_helper_fcmp(cpu_env, cpu_src, cpu_dest);
return;
@@ -5995,6 +6060,7 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s)
uint16_t insn = read_im16(env, s);
opcode_table[insn](env, s, insn);
do_writebacks(s);
+ do_release(s);
}
/* generate intermediate code for basic block 'tb'. */
@@ -6029,6 +6095,8 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
max_insns = TCG_MAX_INSNS;
}
+ init_release_array(dc);
+
gen_tb_start(tb);
do {
pc_offset = dc->pc - pc_start;
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index 1fe21c8539..5be71bc320 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -343,7 +343,7 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo,
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_MICROBLAZE_CPU, cpu_model)
+#define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU
#define cpu_signal_handler cpu_mb_signal_handler
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index 7f8ba5ff3e..cfe1735e0e 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -739,10 +739,9 @@ enum {
int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_MIPS_CPU, cpu_model)
-
#define MIPS_CPU_TYPE_SUFFIX "-" TYPE_MIPS_CPU
#define MIPS_CPU_TYPE_NAME(model) model MIPS_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_MIPS_CPU
bool cpu_supports_cps_smp(const char *cpu_type);
bool cpu_supports_isa(const char *cpu_type, unsigned int isa);
diff --git a/target/moxie/cpu.h b/target/moxie/cpu.h
index d85e1fc061..d40f1e6c45 100644
--- a/target/moxie/cpu.h
+++ b/target/moxie/cpu.h
@@ -119,10 +119,9 @@ void moxie_translate_init(void);
int cpu_moxie_signal_handler(int host_signum, void *pinfo,
void *puc);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_MOXIE_CPU, cpu_model)
-
#define MOXIE_CPU_TYPE_SUFFIX "-" TYPE_MOXIE_CPU
#define MOXIE_CPU_TYPE_NAME(model) model MOXIE_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_MOXIE_CPU
#define cpu_signal_handler cpu_moxie_signal_handler
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index cd4e40d1b4..145796e8ce 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -230,7 +230,7 @@ void nios2_check_interrupts(CPUNios2State *env);
# define TARGET_VIRT_ADDR_SPACE_BITS 32
#endif
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_NIOS2_CPU, cpu_model)
+#define CPU_RESOLVING_TYPE TYPE_NIOS2_CPU
#define cpu_gen_code cpu_nios2_gen_code
#define cpu_signal_handler cpu_nios2_signal_handler
diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h
index 5050b1135c..35cab65f11 100644
--- a/target/openrisc/cpu.h
+++ b/target/openrisc/cpu.h
@@ -389,10 +389,9 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
int *prot, target_ulong address, int rw);
#endif
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_OPENRISC_CPU, cpu_model)
-
#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU
#define OPENRISC_CPU_TYPE_NAME(model) model OPENRISC_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_OPENRISC_CPU
#include "exec/cpu-all.h"
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 7bde1884a1..c621a6bd5e 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1375,10 +1375,9 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp);
int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_POWERPC_CPU, cpu_model)
-
#define POWERPC_CPU_TYPE_SUFFIX "-" TYPE_POWERPC_CPU
#define POWERPC_CPU_TYPE_NAME(model) model POWERPC_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU
#define cpu_signal_handler cpu_ppc_signal_handler
#define cpu_list ppc_cpu_list
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 0a0c090c99..218665b408 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -4526,7 +4526,7 @@ static void gen_tlbie(DisasContext *ctx)
TCGv_i32 t1;
if (ctx->gtse) {
- CHK_SV; /* If gtse is set then tblie is supervisor privileged */
+ CHK_SV; /* If gtse is set then tlbie is supervisor privileged */
} else {
CHK_HV; /* Else hypervisor privileged */
}
@@ -4553,7 +4553,12 @@ static void gen_tlbsync(DisasContext *ctx)
#if defined(CONFIG_USER_ONLY)
GEN_PRIV;
#else
- CHK_HV;
+
+ if (ctx->gtse) {
+ CHK_SV; /* If gtse is set then tlbsync is supervisor privileged */
+ } else {
+ CHK_HV; /* Else hypervisor privileged */
+ }
/* BookS does both ptesync and tlbsync make tlbsync a nop for server */
if (ctx->insns_flags & PPC_BOOKE) {
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 4851890844..9de34d7099 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -391,16 +391,16 @@ static const TypeInfo riscv_cpu_type_info = {
char *riscv_isa_string(RISCVCPU *cpu)
{
int i;
- size_t maxlen = 5 + ctz32(cpu->env.misa);
- char *isa_string = g_new0(char, maxlen);
- snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
+ const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
+ char *isa_str = g_new(char, maxlen);
+ char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
for (i = 0; i < sizeof(riscv_exts); i++) {
if (cpu->env.misa & RV(riscv_exts[i])) {
- isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
-
+ *p++ = qemu_tolower(riscv_exts[i]);
}
}
- return isa_string;
+ *p = '\0';
+ return isa_str;
}
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index cff02a2857..41e06ac0f9 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -46,6 +46,7 @@
#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1")
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 5f357a4e2d..3ee40f08b7 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -719,10 +719,9 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga,
/* helper.c */
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_S390_CPU, cpu_model)
-
#define S390_CPU_TYPE_SUFFIX "-" TYPE_S390_CPU
#define S390_CPU_TYPE_NAME(name) (name S390_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_S390_CPU
/* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 1d5f0da4fe..2741b6803f 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -453,7 +453,7 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info,
Object *obj;
if (info->props) {
- qdict = qobject_to_qdict(info->props);
+ qdict = qobject_to(QDict, info->props);
if (!qdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict");
return;
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 6302cfda3a..541ffc2d97 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -85,18 +85,6 @@ typedef struct SuperHCPUListState {
FILE *file;
} SuperHCPUListState;
-/* Sort alphabetically by type name. */
-static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
- ObjectClass *class_a = (ObjectClass *)a;
- ObjectClass *class_b = (ObjectClass *)b;
- const char *name_a, *name_b;
-
- name_a = object_class_get_name(class_a);
- name_b = object_class_get_name(class_b);
- return strcmp(name_a, name_b);
-}
-
static void superh_cpu_list_entry(gpointer data, gpointer user_data)
{
SuperHCPUListState *s = user_data;
@@ -114,8 +102,7 @@ void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf)
};
GSList *list;
- list = object_class_get_list(TYPE_SUPERH_CPU, false);
- list = g_slist_sort(list, superh_cpu_list_compare);
+ list = object_class_get_list_sorted(TYPE_SUPERH_CPU, false);
g_slist_foreach(list, superh_cpu_list_entry, &s);
g_slist_free(list);
}
diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h
index a649b68d78..775b5743bf 100644
--- a/target/sh4/cpu.h
+++ b/target/sh4/cpu.h
@@ -272,10 +272,9 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr);
void cpu_load_tlb(CPUSH4State * env);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_SUPERH_CPU, cpu_model)
-
#define SUPERH_CPU_TYPE_SUFFIX "-" TYPE_SUPERH_CPU
#define SUPERH_CPU_TYPE_NAME(model) model SUPERH_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_SUPERH_CPU
#define cpu_signal_handler cpu_sh4_signal_handler
#define cpu_list sh4_cpu_list
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 9724134a5b..4972ebcfd4 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -652,12 +652,9 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr,
#endif
int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
-#ifndef NO_CPU_IO_DEFS
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_SPARC_CPU, cpu_model)
-#endif
-
#define SPARC_CPU_TYPE_SUFFIX "-" TYPE_SPARC_CPU
#define SPARC_CPU_TYPE_NAME(model) model SPARC_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_SPARC_CPU
#define cpu_signal_handler cpu_sparc_signal_handler
#define cpu_list sparc_cpu_list
diff --git a/target/tilegx/cpu.h b/target/tilegx/cpu.h
index 71cea04589..238f8d36d7 100644
--- a/target/tilegx/cpu.h
+++ b/target/tilegx/cpu.h
@@ -164,7 +164,7 @@ static inline TileGXCPU *tilegx_env_get_cpu(CPUTLGState *env)
void tilegx_tcg_init(void);
int cpu_tilegx_signal_handler(int host_signum, void *pinfo, void *puc);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_TILEGX_CPU, cpu_model)
+#define CPU_RESOLVING_TYPE TYPE_TILEGX_CPU
#define cpu_signal_handler cpu_tilegx_signal_handler
diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h
index 07b8b59f58..c3665c6ddd 100644
--- a/target/tricore/cpu.h
+++ b/target/tricore/cpu.h
@@ -413,10 +413,9 @@ static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, target_ulong *pc,
*flags = 0;
}
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_TRICORE_CPU, cpu_model)
-
#define TRICORE_CPU_TYPE_SUFFIX "-" TYPE_TRICORE_CPU
#define TRICORE_CPU_TYPE_NAME(model) model TRICORE_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_TRICORE_CPU
/* helpers.c */
int cpu_tricore_handle_mmu_fault(CPUState *cpu, target_ulong address,
diff --git a/target/tricore/helper.c b/target/tricore/helper.c
index 45276d3782..dad7eea085 100644
--- a/target/tricore/helper.c
+++ b/target/tricore/helper.c
@@ -101,7 +101,7 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf)
};
GSList *list;
- list = object_class_get_list(TYPE_TRICORE_CPU, false);
+ list = object_class_get_list_sorted(TYPE_TRICORE_CPU, false);
(*cpu_fprintf)(f, "Available CPUs:\n");
g_slist_foreach(list, tricore_cpu_list_entry, &s);
g_slist_free(list);
diff --git a/target/unicore32/cpu.h b/target/unicore32/cpu.h
index 42e1d52478..735d3ae9dc 100644
--- a/target/unicore32/cpu.h
+++ b/target/unicore32/cpu.h
@@ -164,10 +164,9 @@ static inline int cpu_mmu_index(CPUUniCore32State *env, bool ifetch)
#include "exec/cpu-all.h"
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_UNICORE32_CPU, cpu_model)
-
#define UNICORE32_CPU_TYPE_SUFFIX "-" TYPE_UNICORE32_CPU
#define UNICORE32_CPU_TYPE_NAME(model) model UNICORE32_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_UNICORE32_CPU
static inline void cpu_get_tb_cpu_state(CPUUniCore32State *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
diff --git a/target/xtensa/Makefile.objs b/target/xtensa/Makefile.objs
index e15851521f..2fae785cb2 100644
--- a/target/xtensa/Makefile.objs
+++ b/target/xtensa/Makefile.objs
@@ -1,10 +1,9 @@
-obj-y += xtensa-semi.o
obj-y += core-dc232b.o
obj-y += core-dc233c.o
obj-y += core-de212.o
obj-y += core-fsf.o
obj-y += core-sample_controller.o
-obj-$(CONFIG_SOFTMMU) += monitor.o
+obj-$(CONFIG_SOFTMMU) += monitor.o xtensa-semi.o
obj-y += xtensa-isa.o
obj-y += translate.o op_helper.o helper.o cpu.o
obj-y += gdbstub.o
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index 4573388a45..2b5b537222 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -45,9 +45,13 @@ static void xtensa_cpu_set_pc(CPUState *cs, vaddr value)
static bool xtensa_cpu_has_work(CPUState *cs)
{
+#ifndef CONFIG_USER_ONLY
XtensaCPU *cpu = XTENSA_CPU(cs);
return !cpu->env.runstall && cpu->env.pending_irq_level;
+#else
+ return true;
+#endif
}
/* CPUClass::reset() */
@@ -62,8 +66,16 @@ static void xtensa_cpu_reset(CPUState *s)
env->exception_taken = 0;
env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors];
env->sregs[LITBASE] &= ~1;
+#ifndef CONFIG_USER_ONLY
env->sregs[PS] = xtensa_option_enabled(env->config,
XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
+ env->pending_irq_level = 0;
+#else
+ env->sregs[PS] =
+ (xtensa_option_enabled(env->config,
+ XTENSA_OPTION_WINDOWED_REGISTER) ? PS_WOE : 0) |
+ PS_UM | (3 << PS_RING_SHIFT);
+#endif
env->sregs[VECBASE] = env->config->vecbase;
env->sregs[IBREAKENABLE] = 0;
env->sregs[MEMCTL] = MEMCTL_IL0EN & env->config->memctl_mask;
@@ -73,9 +85,10 @@ static void xtensa_cpu_reset(CPUState *s)
env->sregs[CONFIGID0] = env->config->configid[0];
env->sregs[CONFIGID1] = env->config->configid[1];
- env->pending_irq_level = 0;
+#ifndef CONFIG_USER_ONLY
reset_mmu(env);
s->halted = env->runstall;
+#endif
}
static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model)
@@ -104,11 +117,12 @@ static void xtensa_cpu_disas_set_info(CPUState *cs, disassemble_info *info)
static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
- XtensaCPU *cpu = XTENSA_CPU(dev);
XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(dev);
Error *local_err = NULL;
- xtensa_irq_init(&cpu->env);
+#ifndef CONFIG_USER_ONLY
+ xtensa_irq_init(&XTENSA_CPU(dev)->env);
+#endif
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
@@ -133,11 +147,13 @@ static void xtensa_cpu_initfn(Object *obj)
cs->env_ptr = env;
env->config = xcc->config;
+#ifndef CONFIG_USER_ONLY
env->address_space_er = g_malloc(sizeof(*env->address_space_er));
env->system_er = g_malloc(sizeof(*env->system_er));
memory_region_init_io(env->system_er, NULL, NULL, env, "er",
UINT64_C(0x100000000));
address_space_init(env->address_space_er, env->system_er, "ER");
+#endif
}
static const VMStateDescription vmstate_xtensa_cpu = {
@@ -166,7 +182,9 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
cc->gdb_read_register = xtensa_cpu_gdb_read_register;
cc->gdb_write_register = xtensa_cpu_gdb_write_register;
cc->gdb_stop_before_watchpoint = true;
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ cc->handle_mmu_fault = xtensa_cpu_handle_mmu_fault;
+#else
cc->do_unaligned_access = xtensa_cpu_do_unaligned_access;
cc->get_phys_page_debug = xtensa_cpu_get_phys_page_debug;
cc->do_unassigned_access = xtensa_cpu_do_unassigned_access;
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index 49c2e3cf9a..e9d2e109f7 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -31,6 +31,9 @@
#define ALIGNED_ONLY
#define TARGET_LONG_BITS 32
+/* Xtensa processors have a weak memory model */
+#define TCG_GUEST_DEFAULT_MO (0)
+
#define CPUArchState struct CPUXtensaState
#include "qemu-common.h"
@@ -41,7 +44,11 @@
#define NB_MMU_MODES 4
#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#ifdef CONFIG_USER_ONLY
+#define TARGET_VIRT_ADDR_SPACE_BITS 30
+#else
#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#endif
#define TARGET_PAGE_BITS 12
enum {
@@ -173,6 +180,7 @@ enum {
#define PS_OWB 0xf00
#define PS_OWB_SHIFT 8
+#define PS_OWB_LEN 4
#define PS_CALLINC 0x30000
#define PS_CALLINC_SHIFT 16
@@ -310,6 +318,7 @@ typedef struct xtensa_tlb {
typedef struct XtensaGdbReg {
int targno;
+ unsigned flags;
int type;
int group;
unsigned size;
@@ -434,6 +443,7 @@ typedef struct CPUXtensaState {
} fregs[16];
float_status fp_status;
+#ifndef CONFIG_USER_ONLY
xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE];
xtensa_tlb_entry dtlb[10][MAX_TLB_WAY_SIZE];
unsigned autorefill_idx;
@@ -446,6 +456,7 @@ typedef struct CPUXtensaState {
uint64_t time_base;
uint64_t ccount_time;
uint32_t ccount_base;
+#endif
int exception_taken;
int yield_needed;
@@ -480,6 +491,9 @@ static inline XtensaCPU *xtensa_env_get_cpu(const CPUXtensaState *env)
#define ENV_OFFSET offsetof(XtensaCPU, env)
+
+int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int size,
+ int mmu_idx);
void xtensa_cpu_do_interrupt(CPUState *cpu);
bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request);
void xtensa_cpu_do_unassigned_access(CPUState *cpu, hwaddr addr,
@@ -499,6 +513,7 @@ void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
#define XTENSA_CPU_TYPE_SUFFIX "-" TYPE_XTENSA_CPU
#define XTENSA_CPU_TYPE_NAME(model) model XTENSA_CPU_TYPE_SUFFIX
+#define CPU_RESOLVING_TYPE TYPE_XTENSA_CPU
#ifdef TARGET_WORDS_BIGENDIAN
#define XTENSA_DEFAULT_CPU_MODEL "fsf"
@@ -512,8 +527,6 @@ void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
#define XTENSA_DEFAULT_CPU_NOMMU_TYPE \
XTENSA_CPU_TYPE_NAME(XTENSA_DEFAULT_CPU_NOMMU_MODEL)
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_XTENSA_CPU, cpu_model)
-
void xtensa_translate_init(void);
void xtensa_breakpoint_handler(CPUState *cs);
void xtensa_finalize_config(XtensaConfig *config);
@@ -527,26 +540,9 @@ int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
void xtensa_sync_window_from_phys(CPUXtensaState *env);
void xtensa_sync_phys_from_window(CPUXtensaState *env);
-uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, bool dtlb, uint32_t way);
-void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
- uint32_t *vpn, uint32_t wi, uint32_t *ei);
-int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb,
- uint32_t *pwi, uint32_t *pei, uint8_t *pring);
-void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env,
- xtensa_tlb_entry *entry, bool dtlb,
- unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte);
-void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb,
- unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte);
-int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb,
- uint32_t vaddr, int is_write, int mmu_idx,
- uint32_t *paddr, uint32_t *page_size, unsigned *access);
-void reset_mmu(CPUXtensaState *env);
-void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env);
+void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta);
+void xtensa_restore_owb(CPUXtensaState *env);
void debug_exception_env(CPUXtensaState *new_env, uint32_t cause);
-static inline MemoryRegion *xtensa_get_er_region(CPUXtensaState *env)
-{
- return env->system_er;
-}
static inline void xtensa_select_static_vectors(CPUXtensaState *env,
unsigned n)
@@ -600,6 +596,29 @@ static inline int xtensa_get_cring(const CPUXtensaState *env)
}
}
+#ifndef CONFIG_USER_ONLY
+uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env,
+ bool dtlb, uint32_t way);
+void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb,
+ uint32_t *vpn, uint32_t wi, uint32_t *ei);
+int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb,
+ uint32_t *pwi, uint32_t *pei, uint8_t *pring);
+void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env,
+ xtensa_tlb_entry *entry, bool dtlb,
+ unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte);
+void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb,
+ unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte);
+int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb,
+ uint32_t vaddr, int is_write, int mmu_idx,
+ uint32_t *paddr, uint32_t *page_size, unsigned *access);
+void reset_mmu(CPUXtensaState *env);
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env);
+
+static inline MemoryRegion *xtensa_get_er_region(CPUXtensaState *env)
+{
+ return env->system_er;
+}
+
static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env,
bool dtlb, unsigned wi, unsigned ei)
{
@@ -607,6 +626,7 @@ static inline xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env,
env->dtlb[wi] + ei :
env->itlb[wi] + ei;
}
+#endif
static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env)
{
@@ -619,6 +639,7 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env)
#define MMU_MODE1_SUFFIX _ring1
#define MMU_MODE2_SUFFIX _ring2
#define MMU_MODE3_SUFFIX _ring3
+#define MMU_USER_IDX 3
static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch)
{
diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c
index d78a1b437d..a8ea98d03f 100644
--- a/target/xtensa/gdbstub.c
+++ b/target/xtensa/gdbstub.c
@@ -28,9 +28,14 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
XtensaCPU *cpu = XTENSA_CPU(cs);
CPUXtensaState *env = &cpu->env;
const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n;
+#ifdef CONFIG_USER_ONLY
+ int num_regs = env->config->gdb_regmap.num_core_regs;
+#else
+ int num_regs = env->config->gdb_regmap.num_regs;
+#endif
unsigned i;
- if (n < 0 || n >= env->config->gdb_regmap.num_regs) {
+ if (n < 0 || n >= num_regs) {
return 0;
}
@@ -81,8 +86,13 @@ int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
CPUXtensaState *env = &cpu->env;
uint32_t tmp;
const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n;
+#ifdef CONFIG_USER_ONLY
+ int num_regs = env->config->gdb_regmap.num_core_regs;
+#else
+ int num_regs = env->config->gdb_regmap.num_regs;
+#endif
- if (n < 0 || n >= env->config->gdb_regmap.num_regs) {
+ if (n < 0 || n >= num_regs) {
return 0;
}
diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c
index 5009fecedc..34844eead3 100644
--- a/target/xtensa/helper.c
+++ b/target/xtensa/helper.c
@@ -88,19 +88,31 @@ static void init_libisa(XtensaConfig *config)
void xtensa_finalize_config(XtensaConfig *config)
{
- unsigned i, n = 0;
-
if (config->isa_internal) {
init_libisa(config);
}
- if (config->gdb_regmap.num_regs) {
- return;
- }
- for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) {
- n += (config->gdb_regmap.reg[i].type != 6);
+ if (config->gdb_regmap.num_regs == 0 ||
+ config->gdb_regmap.num_core_regs == 0) {
+ unsigned i;
+ unsigned n_regs = 0;
+ unsigned n_core_regs = 0;
+
+ for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) {
+ if (config->gdb_regmap.reg[i].type != 6) {
+ ++n_regs;
+ if ((config->gdb_regmap.reg[i].flags & 0x1) == 0) {
+ ++n_core_regs;
+ }
+ }
+ }
+ if (config->gdb_regmap.num_regs == 0) {
+ config->gdb_regmap.num_regs = n_regs;
+ }
+ if (config->gdb_regmap.num_core_regs == 0) {
+ config->gdb_regmap.num_core_regs = n_core_regs;
+ }
}
- config->gdb_regmap.num_regs = n;
}
void xtensa_register_core(XtensaConfigList *node)
@@ -161,6 +173,7 @@ void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
+#ifndef CONFIG_USER_ONLY
XtensaCPU *cpu = XTENSA_CPU(cs);
uint32_t paddr;
uint32_t page_size;
@@ -175,8 +188,13 @@ hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
return paddr;
}
return ~0;
+#else
+ return addr;
+#endif
}
+#ifndef CONFIG_USER_ONLY
+
static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
{
if (xtensa_option_enabled(env->config,
@@ -286,6 +304,11 @@ void xtensa_cpu_do_interrupt(CPUState *cs)
}
check_interrupts(env);
}
+#else
+void xtensa_cpu_do_interrupt(CPUState *cs)
+{
+}
+#endif
bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
@@ -297,6 +320,25 @@ bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
return false;
}
+#ifdef CONFIG_USER_ONLY
+
+int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw,
+ int mmu_idx)
+{
+ XtensaCPU *cpu = XTENSA_CPU(cs);
+ CPUXtensaState *env = &cpu->env;
+
+ qemu_log_mask(CPU_LOG_INT,
+ "%s: rw = %d, address = 0x%08" VADDR_PRIx ", size = %d\n",
+ __func__, rw, address, size);
+ env->sregs[EXCVADDR] = address;
+ env->sregs[EXCCAUSE] = rw ? STORE_PROHIBITED_CAUSE : LOAD_PROHIBITED_CAUSE;
+ cs->exception_index = EXC_USER;
+ return 1;
+}
+
+#else
+
static void reset_tlb_mmu_all_ways(CPUXtensaState *env,
const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE])
{
@@ -757,3 +799,4 @@ void xtensa_runstall(CPUXtensaState *env, bool runstall)
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT);
}
}
+#endif
diff --git a/target/xtensa/helper.h b/target/xtensa/helper.h
index cc751c98fb..73444ae02c 100644
--- a/target/xtensa/helper.h
+++ b/target/xtensa/helper.h
@@ -12,9 +12,12 @@ DEF_HELPER_1(restore_owb, void, env)
DEF_HELPER_2(movsp, void, env, i32)
DEF_HELPER_2(wsr_lbeg, void, env, i32)
DEF_HELPER_2(wsr_lend, void, env, i32)
+#ifndef CONFIG_USER_ONLY
DEF_HELPER_1(simcall, void, env)
+#endif
DEF_HELPER_1(dump_state, void, env)
+#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(waiti, void, env, i32, i32)
DEF_HELPER_1(update_ccount, void, env)
DEF_HELPER_2(wsr_ccount, void, env, i32)
@@ -35,6 +38,7 @@ DEF_HELPER_2(wsr_ibreakenable, void, env, i32)
DEF_HELPER_3(wsr_ibreaka, void, env, i32, i32)
DEF_HELPER_3(wsr_dbreaka, void, env, i32, i32)
DEF_HELPER_3(wsr_dbreakc, void, env, i32, i32)
+#endif
DEF_HELPER_2(wur_fcr, void, env, i32)
DEF_HELPER_FLAGS_1(abs_s, TCG_CALL_NO_RWG_SE, f32, f32)
diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c
index 7486b99799..d401105d09 100644
--- a/target/xtensa/op_helper.c
+++ b/target/xtensa/op_helper.c
@@ -36,6 +36,13 @@
#include "qemu/timer.h"
#include "fpu/softfloat.h"
+#ifdef CONFIG_USER_ONLY
+/* tb_invalidate_phys_range */
+#include "accel/tcg/translate-all.h"
+#endif
+
+#ifndef CONFIG_USER_ONLY
+
void xtensa_cpu_do_unaligned_access(CPUState *cs,
vaddr addr, MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
@@ -102,6 +109,17 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr)
}
}
+#else
+
+static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr)
+{
+ mmap_lock();
+ tb_invalidate_phys_range(vaddr, vaddr + 1);
+ mmap_unlock();
+}
+
+#endif
+
void HELPER(exception)(CPUXtensaState *env, uint32_t excp)
{
CPUState *cs = CPU(xtensa_env_get_cpu(env));
@@ -219,21 +237,21 @@ void xtensa_sync_phys_from_window(CPUXtensaState *env)
copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16);
}
-static void rotate_window_abs(CPUXtensaState *env, uint32_t position)
+static void xtensa_rotate_window_abs(CPUXtensaState *env, uint32_t position)
{
xtensa_sync_phys_from_window(env);
env->sregs[WINDOW_BASE] = windowbase_bound(position, env);
xtensa_sync_window_from_phys(env);
}
-static void rotate_window(CPUXtensaState *env, uint32_t delta)
+void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta)
{
- rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta);
+ xtensa_rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta);
}
void HELPER(wsr_windowbase)(CPUXtensaState *env, uint32_t v)
{
- rotate_window_abs(env, v);
+ xtensa_rotate_window_abs(env, v);
}
void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm)
@@ -251,7 +269,7 @@ void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm)
HELPER(window_check)(env, pc, callinc);
}
env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - imm;
- rotate_window(env, callinc);
+ xtensa_rotate_window(env, callinc);
env->sregs[WINDOW_START] |=
windowstart_bit(env->sregs[WINDOW_BASE], env);
}
@@ -266,7 +284,7 @@ void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w)
assert(n <= w);
- rotate_window(env, n);
+ xtensa_rotate_window(env, n);
env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) |
(windowbase << PS_OWB_SHIFT) | PS_EXCM;
env->sregs[EPC1] = env->pc = pc;
@@ -311,7 +329,7 @@ uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc)
ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff);
- rotate_window(env, -n);
+ xtensa_rotate_window(env, -n);
if (windowstart & windowstart_bit(env->sregs[WINDOW_BASE], env)) {
env->sregs[WINDOW_START] &= ~windowstart_bit(owb, env);
} else {
@@ -334,12 +352,17 @@ uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc)
void HELPER(rotw)(CPUXtensaState *env, uint32_t imm4)
{
- rotate_window(env, imm4);
+ xtensa_rotate_window(env, imm4);
+}
+
+void xtensa_restore_owb(CPUXtensaState *env)
+{
+ xtensa_rotate_window_abs(env, (env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT);
}
void HELPER(restore_owb)(CPUXtensaState *env)
{
- rotate_window_abs(env, (env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT);
+ xtensa_restore_owb(env);
}
void HELPER(movsp)(CPUXtensaState *env, uint32_t pc)
@@ -376,6 +399,8 @@ void HELPER(dump_state)(CPUXtensaState *env)
cpu_dump_state(CPU(cpu), stderr, fprintf, 0);
}
+#ifndef CONFIG_USER_ONLY
+
void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel)
{
CPUState *cpu;
@@ -888,6 +913,7 @@ void HELPER(wsr_dbreakc)(CPUXtensaState *env, uint32_t i, uint32_t v)
}
env->sregs[DBREAKC + i] = v;
}
+#endif
void HELPER(wur_fcr)(CPUXtensaState *env, uint32_t v)
{
@@ -1025,12 +1051,18 @@ void HELPER(ule_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b)
uint32_t HELPER(rer)(CPUXtensaState *env, uint32_t addr)
{
+#ifndef CONFIG_USER_ONLY
return address_space_ldl(env->address_space_er, addr,
MEMTXATTRS_UNSPECIFIED, NULL);
+#else
+ return 0;
+#endif
}
void HELPER(wer)(CPUXtensaState *env, uint32_t data, uint32_t addr)
{
+#ifndef CONFIG_USER_ONLY
address_space_stl(env->address_space_er, addr, data,
MEMTXATTRS_UNSPECIFIED, NULL);
+#endif
}
diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h
index 589dd62850..b24ad11fec 100644
--- a/target/xtensa/overlay_tool.h
+++ b/target/xtensa/overlay_tool.h
@@ -25,9 +25,14 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#define XTREG(idx, ofs, bi, sz, al, no, flags, cp, typ, grp, name, \
- a1, a2, a3, a4, a5, a6) \
- { .targno = (no), .type = (typ), .group = (grp), .size = (sz) },
+#define XTREG(idx, ofs, bi, sz, al, no, fl, cp, typ, grp, name, \
+ a1, a2, a3, a4, a5, a6) { \
+ .targno = (no), \
+ .flags = (fl), \
+ .type = (typ), \
+ .group = (grp), \
+ .size = (sz), \
+},
#define XTREG_END { .targno = -1 },
#ifndef XCHAL_HAVE_DEPBITS
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index 671d934ff4..4f6d03059f 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -345,12 +345,14 @@ static void gen_debug_exception(DisasContext *dc, uint32_t cause)
static bool gen_check_privilege(DisasContext *dc)
{
- if (dc->cring) {
- gen_exception_cause(dc, PRIVILEGED_CAUSE);
- dc->is_jmp = DISAS_UPDATE;
- return false;
+#ifndef CONFIG_USER_ONLY
+ if (!dc->cring) {
+ return true;
}
- return true;
+#endif
+ gen_exception_cause(dc, PRIVILEGED_CAUSE);
+ dc->is_jmp = DISAS_UPDATE;
+ return false;
}
static bool gen_check_cpenable(DisasContext *dc, unsigned cp)
@@ -498,6 +500,7 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access)
return true;
}
+#ifndef CONFIG_USER_ONLY
static bool gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr)
{
if (tb_cflags(dc->tb) & CF_USE_ICOUNT) {
@@ -519,14 +522,17 @@ static bool gen_rsr_ptevaddr(DisasContext *dc, TCGv_i32 d, uint32_t sr)
tcg_gen_andi_i32(d, d, 0xfffffffc);
return false;
}
+#endif
static bool gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr)
{
static bool (* const rsr_handler[256])(DisasContext *dc,
TCGv_i32 d, uint32_t sr) = {
+#ifndef CONFIG_USER_ONLY
[CCOUNT] = gen_rsr_ccount,
[INTSET] = gen_rsr_ccount,
[PTEVADDR] = gen_rsr_ptevaddr,
+#endif
};
if (rsr_handler[sr]) {
@@ -582,6 +588,7 @@ static bool gen_wsr_acchi(DisasContext *dc, uint32_t sr, TCGv_i32 s)
return false;
}
+#ifndef CONFIG_USER_ONLY
static bool gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v)
{
gen_helper_wsr_windowbase(cpu_env, v);
@@ -797,6 +804,11 @@ static bool gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v)
}
return ret;
}
+#else
+static void gen_check_interrupts(DisasContext *dc)
+{
+}
+#endif
static bool gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
{
@@ -808,6 +820,7 @@ static bool gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
[BR] = gen_wsr_br,
[LITBASE] = gen_wsr_litbase,
[ACCHI] = gen_wsr_acchi,
+#ifndef CONFIG_USER_ONLY
[WINDOW_BASE] = gen_wsr_windowbase,
[WINDOW_START] = gen_wsr_windowstart,
[PTEVADDR] = gen_wsr_ptevaddr,
@@ -834,6 +847,7 @@ static bool gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
[CCOMPARE] = gen_wsr_ccompare,
[CCOMPARE + 1] = gen_wsr_ccompare,
[CCOMPARE + 2] = gen_wsr_ccompare,
+#endif
};
if (wsr_handler[sr]) {
@@ -878,6 +892,7 @@ static void gen_load_store_alignment(DisasContext *dc, int shift,
}
}
+#ifndef CONFIG_USER_ONLY
static void gen_waiti(DisasContext *dc, uint32_t imm4)
{
TCGv_i32 pc = tcg_const_i32(dc->next_pc);
@@ -894,6 +909,7 @@ static void gen_waiti(DisasContext *dc, uint32_t imm4)
tcg_temp_free(intlevel);
gen_jumpi_check_loop_end(dc, 0);
}
+#endif
static bool gen_window_check1(DisasContext *dc, unsigned r1)
{
@@ -1215,11 +1231,17 @@ void xtensa_cpu_dump_state(CPUState *cs, FILE *f,
(i % 4) == 3 ? '\n' : ' ');
}
+ xtensa_sync_phys_from_window(env);
cpu_fprintf(f, "\n");
for (i = 0; i < env->config->nareg; ++i) {
- cpu_fprintf(f, "AR%02d=%08x%c", i, env->phys_regs[i],
- (i % 4) == 3 ? '\n' : ' ');
+ cpu_fprintf(f, "AR%02d=%08x ", i, env->phys_regs[i]);
+ if (i % 4 == 3) {
+ bool ws = (env->sregs[WINDOW_START] & (1 << (i / 4))) != 0;
+ bool cw = env->sregs[WINDOW_BASE] == i / 4;
+
+ cpu_fprintf(f, "%c%c\n", ws ? '<' : ' ', cw ? '=' : ' ');
+ }
}
if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) {
@@ -1590,12 +1612,14 @@ static void translate_icache(DisasContext *dc, const uint32_t arg[],
{
if ((!par[0] || gen_check_privilege(dc)) &&
gen_window_check1(dc, arg[0]) && par[1]) {
+#ifndef CONFIG_USER_ONLY
TCGv_i32 addr = tcg_temp_new_i32();
tcg_gen_movi_i32(cpu_pc, dc->pc);
tcg_gen_addi_i32(addr, cpu_R[arg[0]], arg[1]);
gen_helper_itlb_hit_test(cpu_env, addr);
tcg_temp_free(addr);
+#endif
}
}
@@ -1604,12 +1628,14 @@ static void translate_itlb(DisasContext *dc, const uint32_t arg[],
{
if (gen_check_privilege(dc) &&
gen_window_check1(dc, arg[0])) {
+#ifndef CONFIG_USER_ONLY
TCGv_i32 dtlb = tcg_const_i32(par[0]);
gen_helper_itlb(cpu_env, cpu_R[arg[0]], dtlb);
/* This could change memory mapping, so exit tb */
gen_jumpi_check_loop_end(dc, -1);
tcg_temp_free(dtlb);
+#endif
}
}
@@ -1658,9 +1684,15 @@ static void translate_ldst(DisasContext *dc, const uint32_t arg[],
gen_load_store_alignment(dc, par[0] & MO_SIZE, addr, par[1]);
}
if (par[2]) {
+ if (par[1]) {
+ tcg_gen_mb(TCG_BAR_STRL | TCG_MO_ALL);
+ }
tcg_gen_qemu_st_tl(cpu_R[arg[0]], addr, dc->cring, par[0]);
} else {
tcg_gen_qemu_ld_tl(cpu_R[arg[0]], addr, dc->cring, par[0]);
+ if (par[1]) {
+ tcg_gen_mb(TCG_BAR_LDAQ | TCG_MO_ALL);
+ }
}
tcg_temp_free(addr);
}
@@ -1817,6 +1849,12 @@ static void translate_mac16(DisasContext *dc, const uint32_t arg[],
}
}
+static void translate_memw(DisasContext *dc, const uint32_t arg[],
+ const uint32_t par[])
+{
+ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
+}
+
static void translate_minmax(DisasContext *dc, const uint32_t arg[],
const uint32_t par[])
{
@@ -1967,11 +2005,13 @@ static void translate_ptlb(DisasContext *dc, const uint32_t arg[],
{
if (gen_check_privilege(dc) &&
gen_window_check2(dc, arg[0], arg[1])) {
+#ifndef CONFIG_USER_ONLY
TCGv_i32 dtlb = tcg_const_i32(par[0]);
tcg_gen_movi_i32(cpu_pc, dc->pc);
gen_helper_ptlb(cpu_R[arg[0]], cpu_env, cpu_R[arg[1]], dtlb);
tcg_temp_free(dtlb);
+#endif
}
}
@@ -2155,8 +2195,10 @@ static void translate_rtlb(DisasContext *dc, const uint32_t arg[],
{
static void (* const helper[])(TCGv_i32 r, TCGv_env env, TCGv_i32 a1,
TCGv_i32 a2) = {
+#ifndef CONFIG_USER_ONLY
gen_helper_rtlb0,
gen_helper_rtlb1,
+#endif
};
if (gen_check_privilege(dc) &&
@@ -2187,29 +2229,33 @@ static void translate_setb_expstate(DisasContext *dc, const uint32_t arg[],
tcg_gen_ori_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], 1u << arg[0]);
}
+#ifdef CONFIG_USER_ONLY
+static void gen_check_atomctl(DisasContext *dc, TCGv_i32 addr)
+{
+}
+#else
+static void gen_check_atomctl(DisasContext *dc, TCGv_i32 addr)
+{
+ TCGv_i32 tpc = tcg_const_i32(dc->pc);
+
+ gen_helper_check_atomctl(cpu_env, tpc, addr);
+ tcg_temp_free(tpc);
+}
+#endif
+
static void translate_s32c1i(DisasContext *dc, const uint32_t arg[],
const uint32_t par[])
{
if (gen_window_check2(dc, arg[0], arg[1])) {
- TCGLabel *label = gen_new_label();
TCGv_i32 tmp = tcg_temp_local_new_i32();
TCGv_i32 addr = tcg_temp_local_new_i32();
- TCGv_i32 tpc;
tcg_gen_mov_i32(tmp, cpu_R[arg[0]]);
tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]);
gen_load_store_alignment(dc, 2, addr, true);
-
- tpc = tcg_const_i32(dc->pc);
- gen_helper_check_atomctl(cpu_env, tpc, addr);
- tcg_gen_qemu_ld32u(cpu_R[arg[0]], addr, dc->cring);
- tcg_gen_brcond_i32(TCG_COND_NE, cpu_R[arg[0]],
- cpu_SR[SCOMPARE1], label);
-
- tcg_gen_qemu_st32(tmp, addr, dc->cring);
-
- gen_set_label(label);
- tcg_temp_free(tpc);
+ gen_check_atomctl(dc, addr);
+ tcg_gen_atomic_cmpxchg_i32(cpu_R[arg[0]], addr, cpu_SR[SCOMPARE1],
+ tmp, dc->cring, MO_32);
tcg_temp_free(addr);
tcg_temp_free(tmp);
}
@@ -2261,11 +2307,14 @@ static void translate_sext(DisasContext *dc, const uint32_t arg[],
static void translate_simcall(DisasContext *dc, const uint32_t arg[],
const uint32_t par[])
{
+#ifndef CONFIG_USER_ONLY
if (semihosting_enabled()) {
if (gen_check_privilege(dc)) {
gen_helper_simcall(cpu_env);
}
- } else {
+ } else
+#endif
+ {
qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n");
gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
}
@@ -2448,7 +2497,9 @@ static void translate_waiti(DisasContext *dc, const uint32_t arg[],
const uint32_t par[])
{
if (gen_check_privilege(dc)) {
+#ifndef CONFIG_USER_ONLY
gen_waiti(dc, arg[0]);
+#endif
}
}
@@ -2457,12 +2508,14 @@ static void translate_wtlb(DisasContext *dc, const uint32_t arg[],
{
if (gen_check_privilege(dc) &&
gen_window_check2(dc, arg[0], arg[1])) {
+#ifndef CONFIG_USER_ONLY
TCGv_i32 dtlb = tcg_const_i32(par[0]);
gen_helper_wtlb(cpu_env, cpu_R[arg[0]], cpu_R[arg[1]], dtlb);
/* This could change memory mapping, so exit tb */
gen_jumpi_check_loop_end(dc, -1);
tcg_temp_free(dtlb);
+#endif
}
}
@@ -2822,7 +2875,7 @@ static const XtensaOpcodeOps core_ops[] = {
.translate = translate_extui,
}, {
.name = "extw",
- .translate = translate_nop,
+ .translate = translate_memw,
}, {
.name = "hwwdtlba",
.translate = translate_ill,
@@ -2939,7 +2992,7 @@ static const XtensaOpcodeOps core_ops[] = {
.par = (const uint32_t[]){TCG_COND_GEU},
}, {
.name = "memw",
- .translate = translate_nop,
+ .translate = translate_memw,
}, {
.name = "min",
.translate = translate_minmax,
diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c
index fc05909d1d..d7e59e79c5 100644
--- a/tcg/i386/tcg-target.inc.c
+++ b/tcg/i386/tcg-target.inc.c
@@ -2696,6 +2696,12 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc,
case INDEX_op_x86_packus_vec:
insn = packus_insn[vece];
goto gen_simd;
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_dup2_vec:
+ /* Constraints have already placed both 32-bit inputs in xmm regs. */
+ insn = OPC_PUNPCKLDQ;
+ goto gen_simd;
+#endif
gen_simd:
tcg_debug_assert(insn != OPC_UD2);
if (type == TCG_TYPE_V256) {
@@ -3045,6 +3051,9 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
case INDEX_op_x86_vperm2i128_vec:
case INDEX_op_x86_punpckl_vec:
case INDEX_op_x86_punpckh_vec:
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_dup2_vec:
+#endif
return &x_x_x;
case INDEX_op_dup_vec:
case INDEX_op_shli_vec:
diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c
index bfe44bba81..22db1590d5 100644
--- a/tcg/tcg-op-gvec.c
+++ b/tcg/tcg-op-gvec.c
@@ -351,6 +351,42 @@ static void gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in)
}
}
+/* Select a supported vector type for implementing an operation on SIZE
+ * bytes. If OP is 0, assume that the real operation to be performed is
+ * required by all backends. Otherwise, make sure than OP can be performed
+ * on elements of size VECE in the selected type. Do not select V64 if
+ * PREFER_I64 is true. Return 0 if no vector type is selected.
+ */
+static TCGType choose_vector_type(TCGOpcode op, unsigned vece, uint32_t size,
+ bool prefer_i64)
+{
+ if (TCG_TARGET_HAS_v256 && check_size_impl(size, 32)) {
+ if (op == 0) {
+ return TCG_TYPE_V256;
+ }
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ * It is hard to imagine a case in which v256 is supported
+ * but v128 is not, but check anyway.
+ */
+ if (tcg_can_emit_vec_op(op, TCG_TYPE_V256, vece)
+ && (size % 32 == 0
+ || tcg_can_emit_vec_op(op, TCG_TYPE_V128, vece))) {
+ return TCG_TYPE_V256;
+ }
+ }
+ if (TCG_TARGET_HAS_v128 && check_size_impl(size, 16)
+ && (op == 0 || tcg_can_emit_vec_op(op, TCG_TYPE_V128, vece))) {
+ return TCG_TYPE_V128;
+ }
+ if (TCG_TARGET_HAS_v64 && !prefer_i64 && check_size_impl(size, 8)
+ && (op == 0 || tcg_can_emit_vec_op(op, TCG_TYPE_V64, vece))) {
+ return TCG_TYPE_V64;
+ }
+ return 0;
+}
+
/* Set OPRSZ bytes at DOFS to replications of IN_32, IN_64 or IN_C.
* Only one of IN_32 or IN_64 may be set;
* IN_C is used if IN_32 and IN_64 are unset.
@@ -376,19 +412,12 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz,
}
}
- type = 0;
- if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32)) {
- type = TCG_TYPE_V256;
- } else if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16)) {
- type = TCG_TYPE_V128;
- } else if (TCG_TARGET_HAS_v64 && check_size_impl(oprsz, 8)
- /* Prefer integer when 64-bit host and no variable dup. */
- && !(TCG_TARGET_REG_BITS == 64 && in_32 == NULL
- && (in_64 == NULL || vece == MO_64))) {
- type = TCG_TYPE_V64;
- }
-
- /* Implement inline with a vector type, if possible. */
+ /* Implement inline with a vector type, if possible.
+ * Prefer integer when 64-bit host and no variable dup.
+ */
+ type = choose_vector_type(0, vece, oprsz,
+ (TCG_TARGET_REG_BITS == 64 && in_32 == NULL
+ && (in_64 == NULL || vece == MO_64)));
if (type != 0) {
TCGv_vec t_vec = tcg_temp_new_vec(type);
@@ -414,21 +443,30 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz,
}
i = 0;
- if (TCG_TARGET_HAS_v256) {
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
for (; i + 32 <= oprsz; i += 32) {
tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V256);
}
- }
- if (TCG_TARGET_HAS_v128) {
+ /* fallthru */
+ case TCG_TYPE_V128:
for (; i + 16 <= oprsz; i += 16) {
tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V128);
}
- }
- if (TCG_TARGET_HAS_v64) {
+ break;
+ case TCG_TYPE_V64:
for (; i < oprsz; i += 8) {
tcg_gen_stl_vec(t_vec, cpu_env, dofs + i, TCG_TYPE_V64);
}
+ break;
+ default:
+ g_assert_not_reached();
}
+
tcg_temp_free_vec(t_vec);
goto done;
}
@@ -484,7 +522,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz,
}
tcg_temp_free_i64(t_64);
goto done;
- }
+ }
}
/* Otherwise implement out of line. */
@@ -866,49 +904,55 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs,
void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs,
uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g)
{
+ TCGType type;
+ uint32_t some;
+
check_size_align(oprsz, maxsz, dofs | aofs);
check_overlap_2(dofs, aofs, maxsz);
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
- /* ??? For maxsz > oprsz, the host may be able to use an opr-sized
- operation, zeroing the balance of the register. We can then
- use a max-sized store to implement the clearing without an extra
- store operation. This is true for aarch64 and x86_64 hosts. */
-
- if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
+ type = 0;
+ if (g->fniv) {
+ type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64);
+ }
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_2_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, g->fniv);
if (some == oprsz) {
- goto done;
+ break;
}
dofs += some;
aofs += some;
oprsz -= some;
maxsz -= some;
- }
-
- if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) {
+ /* fallthru */
+ case TCG_TYPE_V128:
expand_2_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128, g->fniv);
- } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64
- && g->fniv && check_size_impl(oprsz, 8)
- && (!g->opc
- || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) {
+ break;
+ case TCG_TYPE_V64:
expand_2_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64, g->fniv);
- } else if (g->fni8 && check_size_impl(oprsz, 8)) {
- expand_2_i64(dofs, aofs, oprsz, g->fni8);
- } else if (g->fni4 && check_size_impl(oprsz, 4)) {
- expand_2_i32(dofs, aofs, oprsz, g->fni4);
- } else {
- assert(g->fno != NULL);
- tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno);
- return;
+ break;
+
+ case 0:
+ if (g->fni8 && check_size_impl(oprsz, 8)) {
+ expand_2_i64(dofs, aofs, oprsz, g->fni8);
+ } else if (g->fni4 && check_size_impl(oprsz, 4)) {
+ expand_2_i32(dofs, aofs, oprsz, g->fni4);
+ } else {
+ assert(g->fno != NULL);
+ tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno);
+ return;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
}
- done:
if (oprsz < maxsz) {
expand_clr(dofs + oprsz, maxsz - oprsz);
}
@@ -918,53 +962,64 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs,
void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz,
uint32_t maxsz, int64_t c, const GVecGen2i *g)
{
+ TCGType type;
+ uint32_t some;
+
check_size_align(oprsz, maxsz, dofs | aofs);
check_overlap_2(dofs, aofs, maxsz);
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
-
- if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
+ type = 0;
+ if (g->fniv) {
+ type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64);
+ }
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_2i_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256,
c, g->load_dest, g->fniv);
if (some == oprsz) {
- goto done;
+ break;
}
dofs += some;
aofs += some;
oprsz -= some;
maxsz -= some;
- }
-
- if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) {
+ /* fallthru */
+ case TCG_TYPE_V128:
expand_2i_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128,
c, g->load_dest, g->fniv);
- } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64
- && g->fniv && check_size_impl(oprsz, 8)
- && (!g->opc
- || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) {
+ break;
+ case TCG_TYPE_V64:
expand_2i_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64,
c, g->load_dest, g->fniv);
- } else if (g->fni8 && check_size_impl(oprsz, 8)) {
- expand_2i_i64(dofs, aofs, oprsz, c, g->load_dest, g->fni8);
- } else if (g->fni4 && check_size_impl(oprsz, 4)) {
- expand_2i_i32(dofs, aofs, oprsz, c, g->load_dest, g->fni4);
- } else {
- if (g->fno) {
- tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, c, g->fno);
+ break;
+
+ case 0:
+ if (g->fni8 && check_size_impl(oprsz, 8)) {
+ expand_2i_i64(dofs, aofs, oprsz, c, g->load_dest, g->fni8);
+ } else if (g->fni4 && check_size_impl(oprsz, 4)) {
+ expand_2i_i32(dofs, aofs, oprsz, c, g->load_dest, g->fni4);
} else {
- TCGv_i64 tcg_c = tcg_const_i64(c);
- tcg_gen_gvec_2i_ool(dofs, aofs, tcg_c, oprsz, maxsz, c, g->fnoi);
- tcg_temp_free_i64(tcg_c);
+ if (g->fno) {
+ tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, c, g->fno);
+ } else {
+ TCGv_i64 tcg_c = tcg_const_i64(c);
+ tcg_gen_gvec_2i_ool(dofs, aofs, tcg_c, oprsz,
+ maxsz, c, g->fnoi);
+ tcg_temp_free_i64(tcg_c);
+ }
+ return;
}
- return;
+ break;
+
+ default:
+ g_assert_not_reached();
}
- done:
if (oprsz < maxsz) {
expand_clr(dofs + oprsz, maxsz - oprsz);
}
@@ -981,37 +1036,30 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz,
type = 0;
if (g->fniv) {
- if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32)) {
- type = TCG_TYPE_V256;
- } else if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16)) {
- type = TCG_TYPE_V128;
- } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64
- && check_size_impl(oprsz, 8)) {
- type = TCG_TYPE_V64;
- }
+ type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64);
}
if (type != 0) {
TCGv_vec t_vec = tcg_temp_new_vec(type);
+ uint32_t some;
tcg_gen_dup_i64_vec(g->vece, t_vec, c);
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
switch (type) {
case TCG_TYPE_V256:
- {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
- expand_2s_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256,
- t_vec, g->scalar_first, g->fniv);
- if (some == oprsz) {
- break;
- }
- dofs += some;
- aofs += some;
- oprsz -= some;
- maxsz -= some;
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
+ expand_2s_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256,
+ t_vec, g->scalar_first, g->fniv);
+ if (some == oprsz) {
+ break;
}
+ dofs += some;
+ aofs += some;
+ oprsz -= some;
+ maxsz -= some;
/* fallthru */
case TCG_TYPE_V128:
@@ -1055,48 +1103,60 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz,
void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs,
uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g)
{
+ TCGType type;
+ uint32_t some;
+
check_size_align(oprsz, maxsz, dofs | aofs | bofs);
check_overlap_3(dofs, aofs, bofs, maxsz);
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
-
- if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
+ type = 0;
+ if (g->fniv) {
+ type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64);
+ }
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_3_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256,
g->load_dest, g->fniv);
if (some == oprsz) {
- goto done;
+ break;
}
dofs += some;
aofs += some;
bofs += some;
oprsz -= some;
maxsz -= some;
- }
-
- if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) {
+ /* fallthru */
+ case TCG_TYPE_V128:
expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128,
g->load_dest, g->fniv);
- } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64
- && g->fniv && check_size_impl(oprsz, 8)
- && (!g->opc
- || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) {
+ break;
+ case TCG_TYPE_V64:
expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64,
g->load_dest, g->fniv);
- } else if (g->fni8 && check_size_impl(oprsz, 8)) {
- expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8);
- } else if (g->fni4 && check_size_impl(oprsz, 4)) {
- expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4);
- } else {
- assert(g->fno != NULL);
- tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, g->data, g->fno);
+ break;
+
+ case 0:
+ if (g->fni8 && check_size_impl(oprsz, 8)) {
+ expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8);
+ } else if (g->fni4 && check_size_impl(oprsz, 4)) {
+ expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4);
+ } else {
+ assert(g->fno != NULL);
+ tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz,
+ maxsz, g->data, g->fno);
+ return;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
}
- done:
if (oprsz < maxsz) {
expand_clr(dofs + oprsz, maxsz - oprsz);
}
@@ -1106,20 +1166,27 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs,
void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs,
uint32_t oprsz, uint32_t maxsz, const GVecGen4 *g)
{
+ TCGType type;
+ uint32_t some;
+
check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs);
check_overlap_4(dofs, aofs, bofs, cofs, maxsz);
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
-
- if (TCG_TARGET_HAS_v256 && g->fniv && check_size_impl(oprsz, 32)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V256, g->vece))) {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
+ type = 0;
+ if (g->fniv) {
+ type = choose_vector_type(g->opc, g->vece, oprsz, g->prefer_i64);
+ }
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_4_vec(g->vece, dofs, aofs, bofs, cofs, some,
32, TCG_TYPE_V256, g->fniv);
if (some == oprsz) {
- goto done;
+ break;
}
dofs += some;
aofs += some;
@@ -1127,30 +1194,33 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs,
cofs += some;
oprsz -= some;
maxsz -= some;
- }
-
- if (TCG_TARGET_HAS_v128 && g->fniv && check_size_impl(oprsz, 16)
- && (!g->opc || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V128, g->vece))) {
+ /* fallthru */
+ case TCG_TYPE_V128:
expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz,
16, TCG_TYPE_V128, g->fniv);
- } else if (TCG_TARGET_HAS_v64 && !g->prefer_i64
- && g->fniv && check_size_impl(oprsz, 8)
- && (!g->opc
- || tcg_can_emit_vec_op(g->opc, TCG_TYPE_V64, g->vece))) {
+ break;
+ case TCG_TYPE_V64:
expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz,
8, TCG_TYPE_V64, g->fniv);
- } else if (g->fni8 && check_size_impl(oprsz, 8)) {
- expand_4_i64(dofs, aofs, bofs, cofs, oprsz, g->fni8);
- } else if (g->fni4 && check_size_impl(oprsz, 4)) {
- expand_4_i32(dofs, aofs, bofs, cofs, oprsz, g->fni4);
- } else {
- assert(g->fno != NULL);
- tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs,
- oprsz, maxsz, g->data, g->fno);
- return;
+ break;
+
+ case 0:
+ if (g->fni8 && check_size_impl(oprsz, 8)) {
+ expand_4_i64(dofs, aofs, bofs, cofs, oprsz, g->fni8);
+ } else if (g->fni4 && check_size_impl(oprsz, 4)) {
+ expand_4_i32(dofs, aofs, bofs, cofs, oprsz, g->fni4);
+ } else {
+ assert(g->fno != NULL);
+ tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs,
+ oprsz, maxsz, g->data, g->fno);
+ return;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
}
- done:
if (oprsz < maxsz) {
expand_clr(dofs + oprsz, maxsz - oprsz);
}
@@ -2155,6 +2225,8 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs,
[TCG_COND_LTU] = ltu_fn,
[TCG_COND_LEU] = leu_fn,
};
+ TCGType type;
+ uint32_t some;
check_size_align(oprsz, maxsz, dofs | aofs | bofs);
check_overlap_3(dofs, aofs, bofs, maxsz);
@@ -2165,51 +2237,59 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs,
return;
}
- /* Recall that ARM SVE allows vector sizes that are not a power of 2.
- Expand with successively smaller host vector sizes. The intent is
- that e.g. oprsz == 80 would be expanded with 2x32 + 1x16. */
-
- if (TCG_TARGET_HAS_v256 && check_size_impl(oprsz, 32)
- && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V256, vece)) {
- uint32_t some = QEMU_ALIGN_DOWN(oprsz, 32);
+ /* Implement inline with a vector type, if possible.
+ * Prefer integer when 64-bit host and 64-bit comparison.
+ */
+ type = choose_vector_type(INDEX_op_cmp_vec, vece, oprsz,
+ TCG_TARGET_REG_BITS == 64 && vece == MO_64);
+ switch (type) {
+ case TCG_TYPE_V256:
+ /* Recall that ARM SVE allows vector sizes that are not a
+ * power of 2, but always a multiple of 16. The intent is
+ * that e.g. size == 80 would be expanded with 2x32 + 1x16.
+ */
+ some = QEMU_ALIGN_DOWN(oprsz, 32);
expand_cmp_vec(vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, cond);
if (some == oprsz) {
- goto done;
+ break;
}
dofs += some;
aofs += some;
bofs += some;
oprsz -= some;
maxsz -= some;
- }
-
- if (TCG_TARGET_HAS_v128 && check_size_impl(oprsz, 16)
- && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V128, vece)) {
+ /* fallthru */
+ case TCG_TYPE_V128:
expand_cmp_vec(vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, cond);
- } else if (TCG_TARGET_HAS_v64
- && check_size_impl(oprsz, 8)
- && (TCG_TARGET_REG_BITS == 32 || vece != MO_64)
- && tcg_can_emit_vec_op(INDEX_op_cmp_vec, TCG_TYPE_V64, vece)) {
+ break;
+ case TCG_TYPE_V64:
expand_cmp_vec(vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, cond);
- } else if (vece == MO_64 && check_size_impl(oprsz, 8)) {
- expand_cmp_i64(dofs, aofs, bofs, oprsz, cond);
- } else if (vece == MO_32 && check_size_impl(oprsz, 4)) {
- expand_cmp_i32(dofs, aofs, bofs, oprsz, cond);
- } else {
- gen_helper_gvec_3 * const *fn = fns[cond];
-
- if (fn == NULL) {
- uint32_t tmp;
- tmp = aofs, aofs = bofs, bofs = tmp;
- cond = tcg_swap_cond(cond);
- fn = fns[cond];
- assert(fn != NULL);
+ break;
+
+ case 0:
+ if (vece == MO_64 && check_size_impl(oprsz, 8)) {
+ expand_cmp_i64(dofs, aofs, bofs, oprsz, cond);
+ } else if (vece == MO_32 && check_size_impl(oprsz, 4)) {
+ expand_cmp_i32(dofs, aofs, bofs, oprsz, cond);
+ } else {
+ gen_helper_gvec_3 * const *fn = fns[cond];
+
+ if (fn == NULL) {
+ uint32_t tmp;
+ tmp = aofs, aofs = bofs, bofs = tmp;
+ cond = tcg_swap_cond(cond);
+ fn = fns[cond];
+ assert(fn != NULL);
+ }
+ tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, 0, fn[vece]);
+ return;
}
- tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, 0, fn[vece]);
- return;
+ break;
+
+ default:
+ g_assert_not_reached();
}
- done:
if (oprsz < maxsz) {
expand_clr(dofs + oprsz, maxsz - oprsz);
}
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 3467787323..34b96d68f3 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -277,9 +277,15 @@ void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret,
void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
{
- TCGv_i32 t0 = tcg_const_i32(arg2);
- tcg_gen_mul_i32(ret, arg1, t0);
- tcg_temp_free_i32(t0);
+ if (arg2 == 0) {
+ tcg_gen_movi_i32(ret, 0);
+ } else if (is_power_of_2(arg2)) {
+ tcg_gen_shli_i32(ret, arg1, ctz32(arg2));
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_mul_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
}
void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
@@ -1430,9 +1436,15 @@ void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret,
void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
{
- TCGv_i64 t0 = tcg_const_i64(arg2);
- tcg_gen_mul_i64(ret, arg1, t0);
- tcg_temp_free_i64(t0);
+ if (arg2 == 0) {
+ tcg_gen_movi_i64(ret, 0);
+ } else if (is_power_of_2(arg2)) {
+ tcg_gen_shli_i64(ret, arg1, ctz64(arg2));
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_mul_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
}
void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
diff --git a/tests/.gitignore b/tests/.gitignore
index 18e58b2183..fb62d2299b 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -86,6 +86,7 @@ test-thread-pool
test-throttle
test-timed-average
test-uuid
+test-util-sockets
test-visitor-serialization
test-vmstate
test-write-threshold
diff --git a/tests/Makefile.include b/tests/Makefile.include
index ef9b88c369..0b277036df 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -146,6 +146,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
check-unit-$(CONFIG_LINUX) += tests/test-qga$(EXESUF)
endif
check-unit-y += tests/test-timed-average$(EXESUF)
+check-unit-y += tests/test-util-sockets$(EXESUF)
check-unit-y += tests/test-io-task$(EXESUF)
check-unit-y += tests/test-io-channel-socket$(EXESUF)
check-unit-y += tests/test-io-channel-file$(EXESUF)
@@ -399,6 +400,7 @@ check-qtest-s390x-y += tests/virtio-console-test$(EXESUF)
check-qtest-s390x-y += tests/virtio-serial-test$(EXESUF)
check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF)
+check-qtest-generic-y += tests/machine-none-test$(EXESUF)
check-qtest-generic-y += tests/qom-test$(EXESUF)
check-qtest-generic-y += tests/test-hmp$(EXESUF)
@@ -713,9 +715,11 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS)
tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
+tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
+ tests/socket-helpers.o $(test-util-obj-y)
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
- tests/io-channel-helpers.o $(test-io-obj-y)
+ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y)
tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y)
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
@@ -794,6 +798,7 @@ tests/display-vga-test$(EXESUF): tests/display-vga-test.o
tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
tests/qom-test$(EXESUF): tests/qom-test.o
tests/test-hmp$(EXESUF): tests/test-hmp.o
+tests/machine-none-test$(EXESUF): tests/machine-none-test.o
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y)
tests/nvme-test$(EXESUF): tests/nvme-test.o
@@ -926,14 +931,14 @@ $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json
$^ >$*.test.out 2>$*.test.err; \
echo $$? >$*.test.exit, \
"TEST","$*.out")
- @diff $(SRC_PATH)/$*.out $*.test.out
@# Sanitize error messages (make them independent of build directory)
- @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff $(SRC_PATH)/$*.err -
- @diff $(SRC_PATH)/$*.exit $*.test.exit
+ @perl -p -e 's|\Q$(SRC_PATH)\E/||g' $*.test.err | diff -u $(SRC_PATH)/$*.err -
+ @diff -u $(SRC_PATH)/$*.out $*.test.out
+ @diff -u $(SRC_PATH)/$*.exit $*.test.exit
.PHONY: check-tests/qapi-schema/doc-good.texi
check-tests/qapi-schema/doc-good.texi: tests/qapi-schema/doc-good.test.texi
- @diff -q $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
+ @diff -u $(SRC_PATH)/tests/qapi-schema/doc-good.texi $<
.PHONY: check-decodetree
check-decodetree:
diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c
index ece25c694f..5b24cd26c1 100644
--- a/tests/boot-serial-test.c
+++ b/tests/boot-serial-test.c
@@ -75,11 +75,13 @@ typedef struct testdef {
static testdef_t tests[] = {
{ "alpha", "clipper", "", "PCI:" },
{ "ppc", "ppce500", "", "U-Boot" },
- { "ppc", "prep", "", "Open Hack'Ware BIOS" },
+ { "ppc", "prep", "-m 96", "Memory size: 96 MB" },
+ { "ppc", "40p", "-boot d", "Booting from device d" },
{ "ppc", "g3beige", "", "PowerPC,750" },
{ "ppc", "mac99", "", "PowerPC,G4" },
{ "ppc64", "ppce500", "", "U-Boot" },
- { "ppc64", "prep", "", "Open Hack'Ware BIOS" },
+ { "ppc64", "prep", "-boot e", "Booting from device e" },
+ { "ppc64", "40p", "-m 192", "Memory size: 192 MB" },
{ "ppc64", "mac99", "", "PowerPC,970FX" },
{ "ppc64", "pseries", "", "Open Firmware" },
{ "ppc64", "powernv", "-cpu POWER8", "OPAL" },
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index a3faea8bfc..2e73c2f86e 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -51,7 +51,7 @@ static void qdict_put_obj_test(void)
g_assert(qdict_size(qdict) == 1);
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
- qn = qobject_to_qnum(ent->value);
+ qn = qobject_to(QNum, ent->value);
g_assert_cmpint(qnum_get_int(qn), ==, num);
QDECREF(qdict);
@@ -81,7 +81,7 @@ static void qdict_get_test(void)
obj = qdict_get(tests_dict, key);
g_assert(obj != NULL);
- qn = qobject_to_qnum(obj);
+ qn = qobject_to(QNum, obj);
g_assert_cmpint(qnum_get_int(qn), ==, value);
QDECREF(tests_dict);
@@ -216,7 +216,7 @@ static void qdict_del_test(void)
static void qobject_to_qdict_test(void)
{
QDict *tests_dict = qdict_new();
- g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
+ g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict);
QDECREF(tests_dict);
}
@@ -381,9 +381,9 @@ static void qdict_array_split_test(void)
qdict_array_split(test_dict, &test_list);
- dict1 = qobject_to_qdict(qlist_pop(test_list));
- dict2 = qobject_to_qdict(qlist_pop(test_list));
- int1 = qobject_to_qnum(qlist_pop(test_list));
+ dict1 = qobject_to(QDict, qlist_pop(test_list));
+ dict2 = qobject_to(QDict, qlist_pop(test_list));
+ int1 = qobject_to(QNum, qlist_pop(test_list));
g_assert(dict1);
g_assert(dict2);
@@ -450,7 +450,7 @@ static void qdict_array_split_test(void)
qdict_array_split(test_dict, &test_list);
- int1 = qobject_to_qnum(qlist_pop(test_list));
+ int1 = qobject_to(QNum, qlist_pop(test_list));
g_assert(int1);
g_assert(qlist_empty(test_list));
@@ -607,7 +607,7 @@ static void qdict_crumple_test_recursive(void)
qdict_put_str(src, "vnc.acl..name", "acl0");
qdict_put_str(src, "vnc.acl.rule..name", "acl0");
- dst = qobject_to_qdict(qdict_crumple(src, &error_abort));
+ dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
g_assert(dst);
g_assert_cmpint(qdict_size(dst), ==, 1);
@@ -629,14 +629,14 @@ static void qdict_crumple_test_recursive(void)
g_assert(rules);
g_assert_cmpint(qlist_size(rules), ==, 2);
- rule = qobject_to_qdict(qlist_pop(rules));
+ rule = qobject_to(QDict, qlist_pop(rules));
g_assert(rule);
g_assert_cmpint(qdict_size(rule), ==, 2);
g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
QDECREF(rule);
- rule = qobject_to_qdict(qlist_pop(rules));
+ rule = qobject_to(QDict, qlist_pop(rules));
g_assert(rule);
g_assert_cmpint(qdict_size(rule), ==, 2);
g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index a18ea47cb7..997f4d3d2c 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -60,7 +60,7 @@ static void escaped_string(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
@@ -92,7 +92,7 @@ static void simple_string(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
@@ -123,7 +123,7 @@ static void single_quote_string(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
@@ -817,7 +817,7 @@ static void utf8_string(void)
obj = qobject_from_json(json_in, utf8_out ? &error_abort : NULL);
if (utf8_out) {
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, utf8_out);
} else {
@@ -843,7 +843,7 @@ static void utf8_string(void)
*/
if (0 && json_out != json_in) {
obj = qobject_from_json(json_out, &error_abort);
- str = qobject_to_qstring(obj);
+ str = qobject_to(QString, obj);
g_assert(str);
g_assert_cmpstr(qstring_get_str(str), ==, utf8_out);
}
@@ -864,8 +864,8 @@ static void vararg_string(void)
for (i = 0; test_cases[i].decoded; i++) {
QString *str;
- str = qobject_to_qstring(qobject_from_jsonf("%s",
- test_cases[i].decoded));
+ str = qobject_to(QString,
+ qobject_from_jsonf("%s", test_cases[i].decoded));
g_assert(str);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
@@ -893,8 +893,9 @@ static void simple_number(void)
QNum *qnum;
int64_t val;
- qnum = qobject_to_qnum(qobject_from_json(test_cases[i].encoded,
- &error_abort));
+ qnum = qobject_to(QNum,
+ qobject_from_json(test_cases[i].encoded,
+ &error_abort));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, test_cases[i].decoded);
@@ -920,7 +921,7 @@ static void large_number(void)
uint64_t val;
int64_t ival;
- qnum = qobject_to_qnum(qobject_from_json(maxu64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(maxu64, &error_abort));
g_assert(qnum);
g_assert_cmpuint(qnum_get_uint(qnum), ==, 18446744073709551615U);
g_assert(!qnum_get_try_int(qnum, &ival));
@@ -930,7 +931,7 @@ static void large_number(void)
QDECREF(str);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_json(gtu64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort));
g_assert(qnum);
g_assert_cmpfloat(qnum_get_double(qnum), ==, 18446744073709552e3);
g_assert(!qnum_get_try_uint(qnum, &val));
@@ -941,7 +942,7 @@ static void large_number(void)
QDECREF(str);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_json(lti64, &error_abort));
+ qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort));
g_assert(qnum);
g_assert_cmpfloat(qnum_get_double(qnum), ==, -92233720368547758e2);
g_assert(!qnum_get_try_uint(qnum, &val));
@@ -973,7 +974,7 @@ static void float_number(void)
QNum *qnum;
obj = qobject_from_json(test_cases[i].encoded, &error_abort);
- qnum = qobject_to_qnum(obj);
+ qnum = qobject_to(QNum, obj);
g_assert(qnum);
g_assert(qnum_get_double(qnum) == test_cases[i].decoded);
@@ -997,17 +998,17 @@ static void vararg_number(void)
double valuef = 2.323423423;
int64_t val;
- qnum = qobject_to_qnum(qobject_from_jsonf("%d", value));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%d", value));
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_jsonf("%lld", value_ll));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll));
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value_ll);
QDECREF(qnum);
- qnum = qobject_to_qnum(qobject_from_jsonf("%f", valuef));
+ qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef));
g_assert(qnum_get_double(qnum) == valuef);
QDECREF(qnum);
}
@@ -1020,7 +1021,7 @@ static void keyword_literal(void)
QString *str;
obj = qobject_from_json("true", &error_abort);
- qbool = qobject_to_qbool(obj);
+ qbool = qobject_to(QBool, obj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
@@ -1031,7 +1032,7 @@ static void keyword_literal(void)
QDECREF(qbool);
obj = qobject_from_json("false", &error_abort);
- qbool = qobject_to_qbool(obj);
+ qbool = qobject_to(QBool, obj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == false);
@@ -1041,13 +1042,13 @@ static void keyword_literal(void)
QDECREF(qbool);
- qbool = qobject_to_qbool(qobject_from_jsonf("%i", false));
+ qbool = qobject_to(QBool, qobject_from_jsonf("%i", false));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == false);
QDECREF(qbool);
/* Test that non-zero values other than 1 get collapsed to true */
- qbool = qobject_to_qbool(qobject_from_jsonf("%i", 2));
+ qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
QDECREF(qbool);
diff --git a/tests/check-qlist.c b/tests/check-qlist.c
index 259980d523..a1c69ed648 100644
--- a/tests/check-qlist.c
+++ b/tests/check-qlist.c
@@ -56,7 +56,7 @@ static void qobject_to_qlist_test(void)
qlist = qlist_new();
- g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist);
+ g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist);
QDECREF(qlist);
}
@@ -71,7 +71,7 @@ static void iter_func(QObject *obj, void *opaque)
g_assert(opaque == NULL);
- qi = qobject_to_qnum(obj);
+ qi = qobject_to(QNum, obj);
g_assert(qi != NULL);
g_assert(qnum_get_try_int(qi, &val));
diff --git a/tests/check-qlit.c b/tests/check-qlit.c
index 5d0f65b9c7..96bbb06f2c 100644
--- a/tests/check-qlit.c
+++ b/tests/check-qlit.c
@@ -9,9 +9,11 @@
#include "qemu/osdep.h"
+#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qlit.h"
+#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qstring.h"
static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) {
@@ -57,17 +59,43 @@ static void qlit_equal_qobject_test(void)
g_assert(!qlit_equal_qobject(&qlit_foo, qobj));
- qdict_put(qobject_to_qdict(qobj), "bee", qlist_new());
+ qdict_put(qobject_to(QDict, qobj), "bee", qlist_new());
g_assert(!qlit_equal_qobject(&qlit, qobj));
qobject_decref(qobj);
}
+static void qobject_from_qlit_test(void)
+{
+ QObject *obj, *qobj = qobject_from_qlit(&qlit);
+ QDict *qdict;
+ QList *bee;
+
+ qdict = qobject_to(QDict, qobj);
+ g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
+ g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
+ g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
+
+ bee = qdict_get_qlist(qdict, "bee");
+ obj = qlist_pop(bee);
+ g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43);
+ qobject_decref(obj);
+ obj = qlist_pop(bee);
+ g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44);
+ qobject_decref(obj);
+ obj = qlist_pop(bee);
+ g_assert(qbool_get_bool(qobject_to(QBool, obj)));
+ qobject_decref(obj);
+
+ qobject_decref(qobj);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/qlit/equal_qobject", qlit_equal_qobject_test);
+ g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
return g_test_run();
}
diff --git a/tests/check-qnum.c b/tests/check-qnum.c
index 2b667f7ad7..9187da734b 100644
--- a/tests/check-qnum.c
+++ b/tests/check-qnum.c
@@ -126,11 +126,11 @@ static void qobject_to_qnum_test(void)
QNum *qn;
qn = qnum_from_int(0);
- g_assert(qobject_to_qnum(QOBJECT(qn)) == qn);
+ g_assert(qobject_to(QNum, QOBJECT(qn)) == qn);
QDECREF(qn);
qn = qnum_from_double(0);
- g_assert(qobject_to_qnum(QOBJECT(qn)) == qn);
+ g_assert(qobject_to(QNum, QOBJECT(qn)) == qn);
QDECREF(qn);
}
diff --git a/tests/check-qobject.c b/tests/check-qobject.c
index 7a3670643c..7629b8071b 100644
--- a/tests/check-qobject.c
+++ b/tests/check-qobject.c
@@ -275,7 +275,7 @@ static void qobject_is_equal_dict_test(void)
dict_different_null_key, dict_longer, dict_shorter,
dict_nested);
- dict_crumpled = qobject_to_qdict(qdict_crumple(dict_1, &local_err));
+ dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &local_err));
g_assert(!local_err);
check_equal(dict_crumpled, dict_nested);
diff --git a/tests/check-qstring.c b/tests/check-qstring.c
index 112ec08967..9c4dd3f94f 100644
--- a/tests/check-qstring.c
+++ b/tests/check-qstring.c
@@ -79,7 +79,7 @@ static void qobject_to_qstring_test(void)
QString *qstring;
qstring = qstring_from_str("foo");
- g_assert(qobject_to_qstring(QOBJECT(qstring)) == qstring);
+ g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring);
QDECREF(qstring);
}
diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c
index b80058fe98..a01321aced 100644
--- a/tests/device-introspect-test.c
+++ b/tests/device-introspect-test.c
@@ -52,7 +52,7 @@ static QDict *qom_type_index(QList *types)
QListEntry *e;
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
QINCREF(d);
qdict_put(index, name, d);
@@ -85,7 +85,7 @@ static QDict *type_list_find(QList *types, const char *name)
QListEntry *e;
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *ename = qdict_get_str(d, "name");
if (!strcmp(ename, name)) {
return d;
@@ -151,7 +151,7 @@ static void test_qom_list_parents(const char *parent)
index = qom_type_index(types);
QLIST_FOREACH_ENTRY(types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
g_assert(qom_has_parent(index, name, parent));
@@ -173,7 +173,7 @@ static void test_qom_list_fields(void)
non_abstract = qom_list_types(NULL, false);
QLIST_FOREACH_ENTRY(all_types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
bool abstract = qdict_haskey(d, "abstract") ?
qdict_get_bool(d, "abstract") :
@@ -216,8 +216,8 @@ static void test_device_intro_concrete(void)
types = device_type_list(false);
QLIST_FOREACH_ENTRY(types, entry) {
- type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
- "name");
+ type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
+ "name");
g_assert(type);
test_one_device(type);
}
@@ -238,7 +238,7 @@ static void test_abstract_interfaces(void)
index = qom_type_index(all_types);
QLIST_FOREACH_ENTRY(all_types, e) {
- QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *d = qobject_to(QDict, qlist_entry_obj(e));
const char *name = qdict_get_str(d, "name");
/*
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 13c910069b..200b2b9e92 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -430,7 +430,7 @@ static void qmp_response(JSONMessageParser *parser, GQueue *tokens)
}
g_assert(!qmp->response);
- qmp->response = qobject_to_qdict(obj);
+ qmp->response = qobject_to(QDict, obj);
g_assert(qmp->response);
}
@@ -1008,11 +1008,11 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine))
g_assert(list);
for (p = qlist_first(list); p; p = qlist_next(p)) {
- minfo = qobject_to_qdict(qlist_entry_obj(p));
+ minfo = qobject_to(QDict, qlist_entry_obj(p));
g_assert(minfo);
qobj = qdict_get(minfo, "name");
g_assert(qobj);
- qstr = qobject_to_qstring(qobj);
+ qstr = qobject_to(QString, qobj);
g_assert(qstr);
mname = qstring_get_str(qstr);
cb(mname);
diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c
new file mode 100644
index 0000000000..efdd4be986
--- /dev/null
+++ b/tests/machine-none-test.c
@@ -0,0 +1,103 @@
+/*
+ * Machine 'none' tests.
+ *
+ * Copyright (c) 2018 Red Hat Inc.
+ *
+ * Authors:
+ * Igor Mammedov <imammedo@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qemu/cutils.h"
+#include "libqtest.h"
+#include "qapi/qmp/qdict.h"
+
+
+struct arch2cpu {
+ const char *arch;
+ const char *cpu_model;
+};
+
+static struct arch2cpu cpus_map[] = {
+ /* tested targets list */
+ { "arm", "cortex-a15" },
+ { "aarch64", "cortex-a57" },
+ { "x86_64", "qemu64,apic-id=0" },
+ { "i386", "qemu32,apic-id=0" },
+ { "alpha", "ev67" },
+ { "cris", "crisv32" },
+ { "lm32", "lm32-full" },
+ { "m68k", "m5206" },
+ /* FIXME: { "microblaze", "any" }, doesn't work with -M none -cpu any */
+ /* FIXME: { "microblazeel", "any" }, doesn't work with -M none -cpu any */
+ { "mips", "4Kc" },
+ { "mipsel", "4Kc" },
+ { "mips64", "20Kc" },
+ { "mips64el", "20Kc" },
+ { "moxie", "MoxieLite" },
+ { "nios2", "FIXME" },
+ { "or1k", "or1200" },
+ { "ppc", "604" },
+ { "ppc64", "power8e_v2.1" },
+ { "ppcemb", "440epb" },
+ { "s390x", "qemu" },
+ { "sh4", "sh7750r" },
+ { "sh4eb", "sh7751r" },
+ { "sparc", "LEON2" },
+ { "sparc64", "Fujitsu Sparc64" },
+ { "tricore", "tc1796" },
+ { "unicore32", "UniCore-II" },
+ { "xtensa", "dc233c" },
+ { "xtensaeb", "fsf" },
+ { "hppa", "hppa" },
+ { "riscv64", "rv64gcsu-v1.10.0" },
+ { "riscv32", "rv32gcsu-v1.9.1" },
+};
+
+static const char *get_cpu_model_by_arch(const char *arch)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cpus_map); i++) {
+ if (!strcmp(arch, cpus_map[i].arch)) {
+ return cpus_map[i].cpu_model;
+ }
+ }
+ return NULL;
+}
+
+static void test_machine_cpu_cli(void)
+{
+ QDict *response;
+ const char *arch = qtest_get_arch();
+ const char *cpu_model = get_cpu_model_by_arch(arch);
+
+ if (!cpu_model) {
+ if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) {
+ fprintf(stderr, "WARNING: cpu name for target '%s' isn't defined,"
+ " add it to cpus_map\n", arch);
+ }
+ return; /* TODO: die here to force all targets have a test */
+ }
+ global_qtest = qtest_startf("-machine none -cpu '%s'", cpu_model);
+
+ response = qmp("{ 'execute': 'quit' }");
+ g_assert(qdict_haskey(response, "return"));
+ QDECREF(response);
+
+ qtest_quit(global_qtest);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("machine/none/cpu_option", test_machine_cpu_cli);
+
+ return g_test_run();
+}
diff --git a/tests/numa-test.c b/tests/numa-test.c
index 68aca9cb38..0f861d8176 100644
--- a/tests/numa-test.c
+++ b/tests/numa-test.c
@@ -98,7 +98,7 @@ static void test_query_cpus(const void *data)
QDict *cpu, *props;
int64_t cpu_idx, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "CPU"));
g_assert(qdict_haskey(cpu, "props"));
@@ -140,7 +140,7 @@ static void pc_numa_cpu(const void *data)
QDict *cpu, *props;
int64_t socket, core, thread, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
@@ -193,7 +193,7 @@ static void spapr_numa_cpu(const void *data)
QDict *cpu, *props;
int64_t core, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
@@ -236,7 +236,7 @@ static void aarch64_numa_cpu(const void *data)
QDict *cpu, *props;
int64_t thread, node;
- cpu = qobject_to_qdict(e);
+ cpu = qobject_to(QDict, e);
g_assert(qdict_haskey(cpu, "props"));
props = qdict_get_qdict(cpu, "props");
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 67e417e298..10e68b01d9 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -42,7 +42,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type,
- gen, success_response, boxed):
+ gen, success_response, boxed, allow_oob):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s' % \
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index b5f88959aa..640a6dfd10 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -86,11 +86,9 @@ class TestSingleDrive(iotests.QMPTestCase):
result = self.vm.qmp('block-stream', device='drive0')
self.assert_qmp(result, 'return', {})
- result = self.vm.qmp('block-job-pause', device='drive0')
- self.assert_qmp(result, 'return', {})
-
+ self.pause_job('drive0', wait=False)
self.vm.resume_drive('drive0')
- self.pause_job('drive0')
+ self.pause_wait('drive0')
result = self.vm.qmp('query-block-jobs')
offset = self.dictpath(result, 'return[0]/offset')
diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033
index a1d8357331..ee8a1338bb 100755
--- a/tests/qemu-iotests/033
+++ b/tests/qemu-iotests/033
@@ -105,6 +105,7 @@ for align in 512 4k; do
done
done
+_cleanup_test_img
# Trigger truncate that would shrink qcow2 L1 table, which is done by
# clearing one entry (8 bytes) with bdrv_co_pwrite_zeroes()
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
index 8a5d9fd269..3437c11507 100755
--- a/tests/qemu-iotests/055
+++ b/tests/qemu-iotests/055
@@ -86,11 +86,9 @@ class TestSingleDrive(iotests.QMPTestCase):
target=target, sync='full')
self.assert_qmp(result, 'return', {})
- result = self.vm.qmp('block-job-pause', device='drive0')
- self.assert_qmp(result, 'return', {})
-
+ self.pause_job('drive0', wait=False)
self.vm.resume_drive('drive0')
- self.pause_job('drive0')
+ self.pause_wait('drive0')
result = self.vm.qmp('query-block-jobs')
offset = self.dictpath(result, 'return[0]/offset')
@@ -303,13 +301,12 @@ class TestSingleTransaction(iotests.QMPTestCase):
])
self.assert_qmp(result, 'return', {})
- result = self.vm.qmp('block-job-pause', device='drive0')
- self.assert_qmp(result, 'return', {})
+ self.pause_job('drive0', wait=False)
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
self.assert_qmp(result, 'return', {})
- self.pause_job('drive0')
+ self.pause_wait('drive0')
result = self.vm.qmp('query-block-jobs')
offset = self.dictpath(result, 'return[0]/offset')
@@ -534,11 +531,9 @@ class TestDriveCompression(iotests.QMPTestCase):
result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
self.assert_qmp(result, 'return', {})
- result = self.vm.qmp('block-job-pause', device='drive0')
- self.assert_qmp(result, 'return', {})
-
+ self.pause_job('drive0', wait=False)
self.vm.resume_drive('drive0')
- self.pause_job('drive0')
+ self.pause_wait('drive0')
result = self.vm.qmp('query-block-jobs')
offset = self.dictpath(result, 'return[0]/offset')
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index 04f2c3c841..223292175a 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -29,6 +29,26 @@ backing_img = os.path.join(iotests.test_dir, 'backing.img')
test_img = os.path.join(iotests.test_dir, 'test.img')
target_img = os.path.join(iotests.test_dir, 'target.img')
+def img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs):
+ fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt))
+ optargs = []
+ for k,v in kwargs.iteritems():
+ optargs = optargs + ['-o', '%s=%s' % (k,v)]
+ args = ['create', '-f', fmt] + optargs + [fullname, size]
+ iotests.qemu_img(*args)
+ return fullname
+
+def try_remove(img):
+ try:
+ os.remove(img)
+ except OSError:
+ pass
+
+def io_write_patterns(img, patterns):
+ for pattern in patterns:
+ iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
+
+
class TestSyncModesNoneAndTop(iotests.QMPTestCase):
image_len = 64 * 1024 * 1024 # MB
@@ -108,5 +128,172 @@ class TestBeforeWriteNotifier(iotests.QMPTestCase):
event = self.cancel_and_wait()
self.assert_qmp(event, 'data/type', 'backup')
+class BackupTest(iotests.QMPTestCase):
+ def setUp(self):
+ self.vm = iotests.VM()
+ self.test_img = img_create('test')
+ self.dest_img = img_create('dest')
+ self.vm.add_drive(self.test_img)
+ self.vm.launch()
+
+ def tearDown(self):
+ self.vm.shutdown()
+ try_remove(self.test_img)
+ try_remove(self.dest_img)
+
+ def hmp_io_writes(self, drive, patterns):
+ for pattern in patterns:
+ self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
+ self.vm.hmp_qemu_io(drive, 'flush')
+
+ def qmp_backup_and_wait(self, cmd='drive-backup', serror=None,
+ aerror=None, **kwargs):
+ if not self.qmp_backup(cmd, serror, **kwargs):
+ return False
+ return self.qmp_backup_wait(kwargs['device'], aerror)
+
+ def qmp_backup(self, cmd='drive-backup',
+ error=None, **kwargs):
+ self.assertTrue('device' in kwargs)
+ res = self.vm.qmp(cmd, **kwargs)
+ if error:
+ self.assert_qmp(res, 'error/desc', error)
+ return False
+ self.assert_qmp(res, 'return', {})
+ return True
+
+ def qmp_backup_wait(self, device, error=None):
+ event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
+ match={'data': {'device': device}})
+ self.assertNotEqual(event, None)
+ try:
+ failure = self.dictpath(event, 'data/error')
+ except AssertionError:
+ # Backup succeeded.
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
+ return True
+ else:
+ # Failure.
+ self.assert_qmp(event, 'data/error', qerror)
+ return False
+
+ def test_dismiss_false(self):
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
+ sync='full', target=self.dest_img,
+ auto_dismiss=True)
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+
+ def test_dismiss_true(self):
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
+ sync='full', target=self.dest_img,
+ auto_dismiss=False)
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
+ self.assert_qmp(res, 'return', {})
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+
+ def test_dismiss_bad_id(self):
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ res = self.vm.qmp('block-job-dismiss', id='foobar')
+ self.assert_qmp(res, 'error/class', 'DeviceNotActive')
+
+ def test_dismiss_collision(self):
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
+ sync='full', target=self.dest_img,
+ auto_dismiss=False)
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
+ # Leave zombie job un-dismissed, observe a failure:
+ res = self.qmp_backup_and_wait(serror='Need a root block node',
+ device='drive0', format=iotests.imgfmt,
+ sync='full', target=self.dest_img,
+ auto_dismiss=False)
+ self.assertEqual(res, False)
+ # OK, dismiss the zombie.
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
+ self.assert_qmp(res, 'return', {})
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ # Ensure it's really gone.
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
+ sync='full', target=self.dest_img,
+ auto_dismiss=False)
+
+ def dismissal_failure(self, dismissal_opt):
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+ # Give blkdebug something to chew on
+ self.hmp_io_writes('drive0',
+ (('0x9a', 0, 512),
+ ('0x55', '8M', '352k'),
+ ('0x78', '15872k', '1M')))
+ # Add destination node via blkdebug
+ res = self.vm.qmp('blockdev-add',
+ node_name='target0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'blkdebug',
+ 'image': {
+ 'driver': 'file',
+ 'filename': self.dest_img
+ },
+ 'inject-error': [{
+ 'event': 'write_aio',
+ 'errno': 5,
+ 'immediately': False,
+ 'once': True
+ }],
+ })
+ self.assert_qmp(res, 'return', {})
+
+ res = self.qmp_backup(cmd='blockdev-backup',
+ device='drive0', target='target0',
+ on_target_error='stop',
+ sync='full',
+ auto_dismiss=dismissal_opt)
+ self.assertTrue(res)
+ event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
+ match={'data': {'device': 'drive0'}})
+ self.assertNotEqual(event, None)
+ # OK, job should be wedged
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return[0]/status', 'paused')
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
+ self.assert_qmp(res, 'error/desc',
+ "Job 'drive0' in state 'paused' cannot accept"
+ " command verb 'dismiss'")
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return[0]/status', 'paused')
+ # OK, unstick job and move forward.
+ res = self.vm.qmp('block-job-resume', device='drive0')
+ self.assert_qmp(res, 'return', {})
+ # And now we need to wait for it to conclude;
+ res = self.qmp_backup_wait(device='drive0')
+ self.assertTrue(res)
+ if not dismissal_opt:
+ # Job should now be languishing:
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
+ self.assert_qmp(res, 'return', {})
+ res = self.vm.qmp('query-block-jobs')
+ self.assert_qmp(res, 'return', [])
+
+ def test_dismiss_premature(self):
+ self.dismissal_failure(False)
+
+ def test_dismiss_erroneous(self):
+ self.dismissal_failure(True)
+
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2', 'qed'])
diff --git a/tests/qemu-iotests/056.out b/tests/qemu-iotests/056.out
index 8d7e996700..dae404e278 100644
--- a/tests/qemu-iotests/056.out
+++ b/tests/qemu-iotests/056.out
@@ -1,5 +1,5 @@
-...
+.........
----------------------------------------------------------------------
-Ran 3 tests
+Ran 9 tests
OK
diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089
index 0b059aba90..aa1ba4a98e 100755
--- a/tests/qemu-iotests/089
+++ b/tests/qemu-iotests/089
@@ -82,6 +82,26 @@ $QEMU_IO_PROG --cache $CACHEMODE \
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+echo
+echo "=== Testing correct handling of 'backing':null ==="
+echo
+
+_make_test_img -b "$TEST_IMG.base" $IMG_SIZE
+
+# This should read 42
+$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
+
+# This should read 0
+$QEMU_IO -c 'read -P 0 0 512' "json:{\
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ },
+ 'backing': null
+}" | _filter_qemu_io
+
+
# Taken from test 071
echo
echo "=== Testing blkdebug ==="
diff --git a/tests/qemu-iotests/089.out b/tests/qemu-iotests/089.out
index 0bf5a13ec1..89e3e4340a 100644
--- a/tests/qemu-iotests/089.out
+++ b/tests/qemu-iotests/089.out
@@ -19,6 +19,14 @@ Pattern verification failed at offset 0, 512 bytes
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+=== Testing correct handling of 'backing':null ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
=== Testing blkdebug ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
index c189e2833d..8a9b93672b 100644
--- a/tests/qemu-iotests/109.out
+++ b/tests/qemu-iotests/109.out
@@ -19,7 +19,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
@@ -45,7 +45,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
@@ -71,7 +71,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
@@ -97,7 +97,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
@@ -123,7 +123,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
@@ -149,7 +149,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
@@ -174,7 +174,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
@@ -199,7 +199,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
@@ -224,7 +224,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
@@ -249,7 +249,7 @@ read 65536/65536 bytes at offset 0
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
@@ -265,7 +265,7 @@ Automatically detecting the format is dangerous for raw images, write operations
Specify the 'raw' format explicitly to remove the restrictions.
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
@@ -274,7 +274,7 @@ Images are identical.
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
+{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
diff --git a/tests/qemu-iotests/146.out b/tests/qemu-iotests/146.out
index db6b296b9e..1332189d87 100644
--- a/tests/qemu-iotests/146.out
+++ b/tests/qemu-iotests/146.out
@@ -54,7 +54,7 @@ Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
=== Testing Image create, force_size ===
-Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296 force_size=on
+Formatting 'TEST_DIR/IMGFMT-create-test.IMGFMT', fmt=IMGFMT size=4294967296
=== Read created image, default opts ====
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
index adfd02695b..a0fd815483 100755
--- a/tests/qemu-iotests/153
+++ b/tests/qemu-iotests/153
@@ -178,6 +178,18 @@ rm -f "${TEST_IMG}.lnk" &>/dev/null
ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
+echo
+echo "== Active commit to intermediate layer should work when base in use =="
+_launch_qemu -drive format=$IMGFMT,file="${TEST_IMG}.a",id=drive0,if=none \
+ -device virtio-blk,drive=drive0
+
+_send_qemu_cmd $QEMU_HANDLE \
+ "{ 'execute': 'qmp_capabilities' }" \
+ 'return'
+_run_cmd $QEMU_IMG commit -b "${TEST_IMG}.b" "${TEST_IMG}.c"
+
+_cleanup_qemu
+
_launch_qemu
_send_qemu_cmd $QEMU_HANDLE \
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
index 34309cfb20..bb721cb747 100644
--- a/tests/qemu-iotests/153.out
+++ b/tests/qemu-iotests/153.out
@@ -372,6 +372,11 @@ Is another process using the image?
== Symbolic link ==
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image?
+
+== Active commit to intermediate layer should work when base in use ==
+{"return": {}}
+
+_qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c
{"return": {}}
Adding drive
diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169
new file mode 100755
index 0000000000..3a8db91f6f
--- /dev/null
+++ b/tests/qemu-iotests/169
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# Tests for dirty bitmaps migration.
+#
+# Copyright (c) 2016-2017 Virtuozzo International GmbH. 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 os
+import iotests
+import time
+import itertools
+import operator
+import new
+from iotests import qemu_img
+
+
+disk_a = os.path.join(iotests.test_dir, 'disk_a')
+disk_b = os.path.join(iotests.test_dir, 'disk_b')
+size = '1M'
+mig_file = os.path.join(iotests.test_dir, 'mig_file')
+
+
+class TestDirtyBitmapMigration(iotests.QMPTestCase):
+ def tearDown(self):
+ self.vm_a.shutdown()
+ self.vm_b.shutdown()
+ os.remove(disk_a)
+ os.remove(disk_b)
+ os.remove(mig_file)
+
+ def setUp(self):
+ qemu_img('create', '-f', iotests.imgfmt, disk_a, size)
+ qemu_img('create', '-f', iotests.imgfmt, disk_b, size)
+
+ self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a)
+ self.vm_a.launch()
+
+ self.vm_b = iotests.VM(path_suffix='b')
+ self.vm_b.add_incoming("exec: cat '" + mig_file + "'")
+
+ def add_bitmap(self, vm, granularity, persistent):
+ params = {'node': 'drive0',
+ 'name': 'bitmap0',
+ 'granularity': granularity}
+ if persistent:
+ params['persistent'] = True
+ params['autoload'] = True
+
+ result = vm.qmp('block-dirty-bitmap-add', **params)
+ self.assert_qmp(result, 'return', {});
+
+ def get_bitmap_hash(self, vm):
+ result = vm.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap0')
+ return result['return']['sha256']
+
+ def check_bitmap(self, vm, sha256):
+ result = vm.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap0')
+ if sha256:
+ self.assert_qmp(result, 'return/sha256', sha256);
+ else:
+ self.assert_qmp(result, 'error/desc',
+ "Dirty bitmap 'bitmap0' not found");
+
+ def do_test_migration(self, persistent, migrate_bitmaps, online,
+ shared_storage):
+ granularity = 512
+
+ # regions = ((start, count), ...)
+ regions = ((0, 0x10000),
+ (0xf0000, 0x10000),
+ (0xa0201, 0x1000))
+
+ should_migrate = migrate_bitmaps or persistent and shared_storage
+
+ self.vm_b.add_drive(disk_a if shared_storage else disk_b)
+
+ if online:
+ os.mkfifo(mig_file)
+ self.vm_b.launch()
+
+ self.add_bitmap(self.vm_a, granularity, persistent)
+ for r in regions:
+ self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r)
+ sha256 = self.get_bitmap_hash(self.vm_a)
+
+ if migrate_bitmaps:
+ capabilities = [{'capability': 'dirty-bitmaps', 'state': True}]
+
+ result = self.vm_a.qmp('migrate-set-capabilities',
+ capabilities=capabilities)
+ self.assert_qmp(result, 'return', {})
+
+ if online:
+ result = self.vm_b.qmp('migrate-set-capabilities',
+ capabilities=capabilities)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm_a.qmp('migrate-set-capabilities',
+ capabilities=[{'capability': 'events',
+ 'state': True}])
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm_a.qmp('migrate', uri='exec:cat>' + mig_file)
+ while True:
+ event = self.vm_a.event_wait('MIGRATION')
+ if event['data']['status'] == 'completed':
+ break
+
+ if not online:
+ self.vm_a.shutdown()
+ self.vm_b.launch()
+ # TODO enable bitmap capability for vm_b in this case
+
+ self.vm_b.event_wait("RESUME", timeout=10.0)
+
+ self.check_bitmap(self.vm_b, sha256 if should_migrate else False)
+
+ if should_migrate:
+ self.vm_b.shutdown()
+ self.vm_b.launch()
+ self.check_bitmap(self.vm_b, sha256 if persistent else False)
+
+
+def inject_test_case(klass, name, method, *args, **kwargs):
+ mc = operator.methodcaller(method, *args, **kwargs)
+ setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass))
+
+for cmb in list(itertools.product((True, False), repeat=3)):
+ name = ('_' if cmb[0] else '_not_') + 'persistent_'
+ name += ('_' if cmb[1] else '_not_') + 'migbitmap_'
+ name += '_online' if cmb[2] else '_offline'
+
+ # TODO fix shared-storage bitmap migration and enable cases for it
+ args = list(cmb) + [False]
+
+ inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration',
+ *args)
+
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/169.out
new file mode 100644
index 0000000000..594c16f49f
--- /dev/null
+++ b/tests/qemu-iotests/169.out
@@ -0,0 +1,5 @@
+........
+----------------------------------------------------------------------
+Ran 8 tests
+
+OK
diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181
index 0c91e8f9de..5e767c6195 100755
--- a/tests/qemu-iotests/181
+++ b/tests/qemu-iotests/181
@@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
_supported_fmt generic
# Formats that do not support live migration
-_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat
+_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat parallels
_supported_proto generic
_supported_os Linux
diff --git a/tests/qemu-iotests/199 b/tests/qemu-iotests/199
new file mode 100755
index 0000000000..651e8df5d9
--- /dev/null
+++ b/tests/qemu-iotests/199
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+#
+# Tests for dirty bitmaps postcopy migration.
+#
+# Copyright (c) 2016-2017 Virtuozzo International GmbH. 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 os
+import iotests
+import time
+from iotests import qemu_img
+
+disk_a = os.path.join(iotests.test_dir, 'disk_a')
+disk_b = os.path.join(iotests.test_dir, 'disk_b')
+size = '256G'
+fifo = os.path.join(iotests.test_dir, 'mig_fifo')
+
+class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase):
+
+ def tearDown(self):
+ self.vm_a.shutdown()
+ self.vm_b.shutdown()
+ os.remove(disk_a)
+ os.remove(disk_b)
+ os.remove(fifo)
+
+ def setUp(self):
+ os.mkfifo(fifo)
+ qemu_img('create', '-f', iotests.imgfmt, disk_a, size)
+ qemu_img('create', '-f', iotests.imgfmt, disk_b, size)
+ self.vm_a = iotests.VM(path_suffix='a').add_drive(disk_a)
+ self.vm_b = iotests.VM(path_suffix='b').add_drive(disk_b)
+ self.vm_b.add_incoming("exec: cat '" + fifo + "'")
+ self.vm_a.launch()
+ self.vm_b.launch()
+
+ def test_postcopy(self):
+ write_size = 0x40000000
+ granularity = 512
+ chunk = 4096
+
+ result = self.vm_a.qmp('block-dirty-bitmap-add', node='drive0',
+ name='bitmap', granularity=granularity)
+ self.assert_qmp(result, 'return', {});
+
+ s = 0
+ while s < write_size:
+ self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
+ s += 0x10000
+ s = 0x8000
+ while s < write_size:
+ self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
+ s += 0x10000
+
+ result = self.vm_a.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap')
+ sha256 = result['return']['sha256']
+
+ result = self.vm_a.qmp('block-dirty-bitmap-clear', node='drive0',
+ name='bitmap')
+ self.assert_qmp(result, 'return', {});
+ s = 0
+ while s < write_size:
+ self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
+ s += 0x10000
+
+ bitmaps_cap = {'capability': 'dirty-bitmaps', 'state': True}
+ events_cap = {'capability': 'events', 'state': True}
+
+ result = self.vm_a.qmp('migrate-set-capabilities',
+ capabilities=[bitmaps_cap, events_cap])
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm_b.qmp('migrate-set-capabilities',
+ capabilities=[bitmaps_cap])
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm_a.qmp('migrate', uri='exec:cat>' + fifo)
+ self.assert_qmp(result, 'return', {})
+
+ result = self.vm_a.qmp('migrate-start-postcopy')
+ self.assert_qmp(result, 'return', {})
+
+ while True:
+ event = self.vm_a.event_wait('MIGRATION')
+ if event['data']['status'] == 'completed':
+ break
+
+ s = 0x8000
+ while s < write_size:
+ self.vm_b.hmp_qemu_io('drive0', 'write %d %d' % (s, chunk))
+ s += 0x10000
+
+ result = self.vm_b.qmp('query-block');
+ while len(result['return'][0]['dirty-bitmaps']) > 1:
+ time.sleep(2)
+ result = self.vm_b.qmp('query-block');
+
+ result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256',
+ node='drive0', name='bitmap')
+
+ self.assert_qmp(result, 'return/sha256', sha256);
+
+if __name__ == '__main__':
+ iotests.main(supported_fmts=['qcow2'], supported_cache_modes=['none'])
diff --git a/tests/qemu-iotests/199.out b/tests/qemu-iotests/199.out
new file mode 100644
index 0000000000..ae1213e6f8
--- /dev/null
+++ b/tests/qemu-iotests/199.out
@@ -0,0 +1,5 @@
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208
new file mode 100755
index 0000000000..4e82b96c82
--- /dev/null
+++ b/tests/qemu-iotests/208
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 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: Stefan Hajnoczi <stefanha@redhat.com>
+#
+# Check that the runtime NBD server does not crash when stopped after
+# blockdev-snapshot-sync.
+
+import iotests
+
+with iotests.FilePath('disk.img') as disk_img_path, \
+ iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \
+ iotests.FilePath('nbd.sock') as nbd_sock_path, \
+ iotests.VM() as vm:
+
+ img_size = '10M'
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk_img_path, img_size)
+
+ iotests.log('Launching VM...')
+ (vm.add_drive(disk_img_path, 'node-name=drive0-node', interface='none')
+ .launch())
+
+ iotests.log('Starting NBD server...')
+ iotests.log(vm.qmp('nbd-server-start', addr={
+ "type": "unix",
+ "data": {
+ "path": nbd_sock_path,
+ }
+ }))
+
+ iotests.log('Adding NBD export...')
+ iotests.log(vm.qmp('nbd-server-add', device='drive0-node', writable=True))
+
+ iotests.log('Creating external snapshot...')
+ iotests.log(vm.qmp('blockdev-snapshot-sync',
+ node_name='drive0-node',
+ snapshot_node_name='drive0-snapshot-node',
+ snapshot_file=disk_snapshot_img_path))
+
+ iotests.log('Stopping NBD server...')
+ iotests.log(vm.qmp('nbd-server-stop'))
diff --git a/tests/qemu-iotests/208.out b/tests/qemu-iotests/208.out
new file mode 100644
index 0000000000..3687e9d0dd
--- /dev/null
+++ b/tests/qemu-iotests/208.out
@@ -0,0 +1,9 @@
+Launching VM...
+Starting NBD server...
+{u'return': {}}
+Adding NBD export...
+{u'return': {}}
+Creating external snapshot...
+{u'return': {}}
+Stopping NBD server...
+{u'return': {}}
diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209
new file mode 100755
index 0000000000..259e991ec6
--- /dev/null
+++ b/tests/qemu-iotests/209
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+#
+# Tests for NBD BLOCK_STATUS extension
+#
+# Copyright (c) 2018 Virtuozzo International GmbH
+#
+# 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 iotests
+from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \
+ file_path
+
+iotests.verify_image_format(supported_fmts=['qcow2'])
+
+disk, nbd_sock = file_path('disk', 'nbd-sock')
+nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
+
+qemu_img_create('-f', iotests.imgfmt, disk, '1M')
+qemu_io('-f', iotests.imgfmt, '-c', 'write 0 512K', disk)
+
+qemu_nbd('-k', nbd_sock, '-x', 'exp', '-f', iotests.imgfmt, disk)
+qemu_img_verbose('map', '-f', 'raw', '--output=json', nbd_uri)
diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out
new file mode 100644
index 0000000000..0d29724e84
--- /dev/null
+++ b/tests/qemu-iotests/209.out
@@ -0,0 +1,2 @@
+[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true},
+{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false}]
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
new file mode 100755
index 0000000000..96a5213e77
--- /dev/null
+++ b/tests/qemu-iotests/210
@@ -0,0 +1,210 @@
+#!/bin/bash
+#
+# Test luks and file image creation
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=kwolf@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+status=1 # failure is the default!
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt luks
+_supported_proto file
+_supported_os Linux
+
+function do_run_qemu()
+{
+ echo Testing: "$@"
+ $QEMU -nographic -qmp stdio -serial none "$@"
+ echo
+}
+
+function run_qemu()
+{
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
+ | _filter_qemu | _filter_imgfmt \
+ | _filter_actual_image_size
+}
+
+echo
+echo "=== Successful image creation (defaults) ==="
+echo
+
+size=$((128 * 1024 * 1024))
+
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG_FILE",
+ "size": 0
+ }
+}
+{ "execute": "blockdev-add",
+ "arguments": {
+ "driver": "file",
+ "node-name": "imgfile",
+ "filename": "$TEST_IMG_FILE"
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "imgfile",
+ "key-secret": "keysec0",
+ "size": $size,
+ "iter-time": 10
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific | _filter_img_info --format-specific
+
+echo
+echo "=== Successful image creation (with non-default options) ==="
+echo
+
+# Choose a different size to show that we got a new image
+size=$((64 * 1024 * 1024))
+
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "file",
+ "filename": "$TEST_IMG_FILE",
+ "size": 0
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": {
+ "driver": "file",
+ "filename": "$TEST_IMG_FILE"
+ },
+ "size": $size,
+ "key-secret": "keysec0",
+ "cipher-alg": "twofish-128",
+ "cipher-mode": "ctr",
+ "ivgen-alg": "plain64",
+ "ivgen-hash-alg": "md5",
+ "hash-alg": "sha1",
+ "iter-time": 10
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info --format-specific | _filter_img_info --format-specific
+
+echo
+echo "=== Invalid BlockdevRef ==="
+echo
+
+run_qemu <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "this doesn't exist",
+ "size": $size
+ }
+}
+{ "execute": "quit" }
+EOF
+
+echo
+echo "=== Zero size ==="
+echo
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
+ -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "key-secret": "keysec0",
+ "size": 0,
+ "iter-time": 10
+ }
+}
+{ "execute": "quit" }
+EOF
+
+_img_info | _filter_img_info
+
+
+echo
+echo "=== Invalid sizes ==="
+echo
+
+# TODO Negative image sizes aren't handled correctly, but this is a problem
+# with QAPI's implementation of the 'size' type and affects other commands as
+# well. Once this is fixed, we may want to add a test case here.
+
+# 1. 2^64 - 512
+# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
+# 3. 2^63 - 512 (generally valid, but with the crypto header the file will
+# exceed 63 bits)
+
+run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
+ -object secret,id=keysec0,data="foo" <<EOF
+{ "execute": "qmp_capabilities" }
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "key-secret": "keysec0",
+ "size": 18446744073709551104
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "key-secret": "keysec0",
+ "size": 9223372036854775808
+ }
+}
+{ "execute": "x-blockdev-create",
+ "arguments": {
+ "driver": "$IMGFMT",
+ "file": "node0",
+ "key-secret": "keysec0",
+ "size": 9223372036854775296
+ }
+}
+{ "execute": "quit" }
+EOF
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out
new file mode 100644
index 0000000000..8fcab65909
--- /dev/null
+++ b/tests/qemu-iotests/210.out
@@ -0,0 +1,136 @@
+QA output created by 210
+
+=== Successful image creation (defaults) ===
+
+Testing: -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
+file format: IMGFMT
+virtual size: 128M (134217728 bytes)
+Format specific information:
+ ivgen alg: plain64
+ hash alg: sha256
+ cipher alg: aes-256
+ uuid: 00000000-0000-0000-0000-000000000000
+ cipher mode: xts
+ slots:
+ [0]:
+ active: true
+ iters: 1024
+ key offset: 4096
+ stripes: 4000
+ [1]:
+ active: false
+ key offset: 262144
+ [2]:
+ active: false
+ key offset: 520192
+ [3]:
+ active: false
+ key offset: 778240
+ [4]:
+ active: false
+ key offset: 1036288
+ [5]:
+ active: false
+ key offset: 1294336
+ [6]:
+ active: false
+ key offset: 1552384
+ [7]:
+ active: false
+ key offset: 1810432
+ payload offset: 2068480
+ master key iters: 1024
+
+=== Successful image creation (with non-default options) ===
+
+Testing: -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+Format specific information:
+ ivgen alg: plain64
+ hash alg: sha1
+ cipher alg: twofish-128
+ uuid: 00000000-0000-0000-0000-000000000000
+ cipher mode: ctr
+ slots:
+ [0]:
+ active: true
+ iters: 1024
+ key offset: 4096
+ stripes: 4000
+ [1]:
+ active: false
+ key offset: 69632
+ [2]:
+ active: false
+ key offset: 135168
+ [3]:
+ active: false
+ key offset: 200704
+ [4]:
+ active: false
+ key offset: 266240
+ [5]:
+ active: false
+ key offset: 331776
+ [6]:
+ active: false
+ key offset: 397312
+ [7]:
+ active: false
+ key offset: 462848
+ payload offset: 528384
+ master key iters: 1024
+
+=== Invalid BlockdevRef ===
+
+Testing:
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+
+=== Zero size ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
+file format: IMGFMT
+virtual size: 0 (0 bytes)
+
+=== Invalid sizes ===
+
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo
+QMP_VERSION
+{"return": {}}
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
+
+*** done
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
index e6b6ff7a04..ec8033350d 100755
--- a/tests/qemu-iotests/check
+++ b/tests/qemu-iotests/check
@@ -92,7 +92,7 @@ set_prog_path()
{
p=`command -v $1 2> /dev/null`
if [ -n "$p" -a -x "$p" ]; then
- realpath -- "$(type -p "$p")"
+ type -p "$p"
else
return 1
fi
@@ -284,7 +284,6 @@ testlist options
-parallels)
IMGFMT=parallels
- IMGFMT_GENERIC=false
xpand=false
;;
@@ -555,7 +554,7 @@ then
[ "$QEMU_PROG" = "" ] && _init_error "qemu not found"
fi
fi
-export QEMU_PROG=$(realpath -- "$(type -p "$QEMU_PROG")")
+export QEMU_PROG="$(type -p "$QEMU_PROG")"
if [ -z "$QEMU_IMG_PROG" ]; then
if [ -x "$build_iotests/qemu-img" ]; then
@@ -566,7 +565,7 @@ if [ -z "$QEMU_IMG_PROG" ]; then
_init_error "qemu-img not found"
fi
fi
-export QEMU_IMG_PROG=$(realpath -- "$(type -p "$QEMU_IMG_PROG")")
+export QEMU_IMG_PROG="$(type -p "$QEMU_IMG_PROG")"
if [ -z "$QEMU_IO_PROG" ]; then
if [ -x "$build_iotests/qemu-io" ]; then
@@ -577,7 +576,7 @@ if [ -z "$QEMU_IO_PROG" ]; then
_init_error "qemu-io not found"
fi
fi
-export QEMU_IO_PROG=$(realpath -- "$(type -p "$QEMU_IO_PROG")")
+export QEMU_IO_PROG="$(type -p "$QEMU_IO_PROG")"
if [ -z $QEMU_NBD_PROG ]; then
if [ -x "$build_iotests/qemu-nbd" ]; then
@@ -588,7 +587,7 @@ if [ -z $QEMU_NBD_PROG ]; then
_init_error "qemu-nbd not found"
fi
fi
-export QEMU_NBD_PROG=$(realpath -- "$(type -p "$QEMU_NBD_PROG")")
+export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")"
if [ -z "$QEMU_VXHS_PROG" ]; then
export QEMU_VXHS_PROG="`set_prog_path qnio_server`"
@@ -812,7 +811,7 @@ do
else
echo " - output mismatch (see $seq.out.bad)"
mv $tmp.out $seq.out.bad
- $diff -w "$reference" $(realpath $seq.out.bad)
+ $diff -w "$reference" "$PWD"/$seq.out.bad
err=true
fi
fi
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index 6fa0495e10..9a65a11026 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -332,7 +332,7 @@ _img_info()
discard=0
regex_json_spec_start='^ *"format-specific": \{'
- $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \
+ $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index c401791fcd..efe0e958f2 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -169,6 +169,7 @@
162 auto quick
163 rw auto quick
165 rw auto quick
+169 rw auto quick
170 rw auto quick
171 rw auto quick
172 auto
@@ -196,6 +197,7 @@
196 rw auto quick
197 rw auto quick
198 rw auto
+199 rw auto
200 rw auto
201 rw auto migration
202 rw auto quick
@@ -204,3 +206,6 @@
205 rw auto quick
206 rw auto
207 rw auto
+208 rw auto quick
+209 rw auto quick
+210 rw auto
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 1bcc9ca57d..b5d7945af8 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -23,12 +23,14 @@ import subprocess
import string
import unittest
import sys
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
-import qtest
import struct
import json
import signal
import logging
+import atexit
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
+import qtest
# This will not work if arguments contain spaces but is necessary if we
@@ -249,6 +251,37 @@ class FilePath(object):
return False
+def file_path_remover():
+ for path in reversed(file_path_remover.paths):
+ try:
+ os.remove(path)
+ except OSError:
+ pass
+
+
+def file_path(*names):
+ ''' Another way to get auto-generated filename that cleans itself up.
+
+ Use is as simple as:
+
+ img_a, img_b = file_path('a.img', 'b.img')
+ sock = file_path('socket')
+ '''
+
+ if not hasattr(file_path_remover, 'paths'):
+ file_path_remover.paths = []
+ atexit.register(file_path_remover)
+
+ paths = []
+ for name in names:
+ filename = '{0}-{1}'.format(os.getpid(), name)
+ path = os.path.join(test_dir, filename)
+ file_path_remover.paths.append(path)
+ paths.append(path)
+
+ return paths[0] if len(paths) == 1 else paths
+
+
class VM(qtest.QEMUQtestMachine):
'''A QEMU VM'''
@@ -473,10 +506,7 @@ class QMPTestCase(unittest.TestCase):
event = self.wait_until_completed(drive=drive)
self.assert_qmp(event, 'data/type', 'mirror')
- def pause_job(self, job_id='job0'):
- result = self.vm.qmp('block-job-pause', device=job_id)
- self.assert_qmp(result, 'return', {})
-
+ def pause_wait(self, job_id='job0'):
with Timeout(1, "Timeout waiting for job to pause"):
while True:
result = self.vm.qmp('query-block-jobs')
@@ -484,6 +514,13 @@ class QMPTestCase(unittest.TestCase):
if job['device'] == job_id and job['paused'] == True and job['busy'] == False:
return job
+ def pause_job(self, job_id='job0', wait=True):
+ result = self.vm.qmp('block-job-pause', device=job_id)
+ self.assert_qmp(result, 'return', {})
+ if wait:
+ return self.pause_wait(job_id)
+ return result
+
def notrun(reason):
'''Skip this test suite'''
@@ -504,6 +541,10 @@ def verify_platform(supported_oses=['linux']):
if True not in [sys.platform.startswith(x) for x in supported_oses]:
notrun('not suitable for this OS: %s' % sys.platform)
+def verify_cache_mode(supported_cache_modes=[]):
+ if supported_cache_modes and (cachemode not in supported_cache_modes):
+ notrun('not suitable for this cache mode: %s' % cachemode)
+
def supports_quorum():
return 'quorum' in qemu_img_pipe('--help')
@@ -512,7 +553,7 @@ def verify_quorum():
if not supports_quorum():
notrun('quorum support missing')
-def main(supported_fmts=[], supported_oses=['linux']):
+def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[]):
'''Run tests'''
global debug
@@ -529,6 +570,7 @@ def main(supported_fmts=[], supported_oses=['linux']):
verbosity = 1
verify_image_format(supported_fmts)
verify_platform(supported_oses)
+ verify_cache_mode(supported_cache_modes)
# We need to filter out the time taken from the output so that qemu-iotest
# can reliably diff the results against master output.
diff --git a/tests/qmp-test.c b/tests/qmp-test.c
index 22445d9ec2..07c0b87e27 100644
--- a/tests/qmp-test.c
+++ b/tests/qmp-test.c
@@ -20,6 +20,7 @@
#include "qapi/qobject-input-visitor.h"
#include "qapi/util.h"
#include "qapi/visitor.h"
+#include "qapi/qmp/qstring.h"
const char common_args[] = "-nodefaults -machine none";
@@ -79,6 +80,9 @@ static void test_qmp_protocol(void)
QDict *resp, *q, *ret;
QList *capabilities;
QTestState *qts;
+ const QListEntry *entry;
+ QString *qstr;
+ int i;
qts = qtest_init_without_qmp_handshake(common_args);
@@ -88,7 +92,12 @@ static void test_qmp_protocol(void)
g_assert(q);
test_version(qdict_get(q, "version"));
capabilities = qdict_get_qlist(q, "capabilities");
- g_assert(capabilities && qlist_empty(capabilities));
+ g_assert(capabilities);
+ entry = qlist_first(capabilities);
+ g_assert(entry);
+ qstr = qobject_to(QString, entry->value);
+ g_assert(qstr);
+ g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
QDECREF(resp);
/* Test valid command before handshake */
@@ -131,9 +140,94 @@ static void test_qmp_protocol(void)
g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
QDECREF(resp);
+ /*
+ * Test command batching. In current test OOB is not enabled, we
+ * should be able to run as many commands in batch as we like.
+ * Using 16 (>8, which is OOB queue length) to make sure OOB won't
+ * break existing clients. Note: this test does not control the
+ * scheduling of QEMU's QMP command processing threads so it may
+ * not really trigger batching inside QEMU. This is just a
+ * best-effort test.
+ */
+ for (i = 0; i < 16; i++) {
+ qtest_async_qmp(qts, "{ 'execute': 'query-version' }");
+ }
+ /* Verify the replies to make sure no command is dropped. */
+ for (i = 0; i < 16; i++) {
+ resp = qtest_qmp_receive(qts);
+ /* It should never be dropped. Each of them should be a reply. */
+ g_assert(qdict_haskey(resp, "return"));
+ g_assert(!qdict_haskey(resp, "event"));
+ QDECREF(resp);
+ }
+
qtest_quit(qts);
}
+/* Tests for Out-Of-Band support. */
+static void test_qmp_oob(void)
+{
+ QDict *resp;
+ int acks = 0;
+ const char *cmd_id;
+
+ global_qtest = qtest_init_without_qmp_handshake(common_args);
+
+ /* Ignore the greeting message. */
+ resp = qmp_receive();
+ g_assert(qdict_get_qdict(resp, "QMP"));
+ QDECREF(resp);
+
+ /* Try a fake capability, it should fail. */
+ resp = qmp("{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /* Now, enable OOB in current QMP session, it should succeed. */
+ resp = qmp("{ 'execute': 'qmp_capabilities', "
+ " 'arguments': { 'enable': [ 'oob' ] } }");
+ g_assert(qdict_haskey(resp, "return"));
+ QDECREF(resp);
+
+ /*
+ * Try any command that does not support OOB but with OOB flag. We
+ * should get failure.
+ */
+ resp = qmp("{ 'execute': 'query-cpus',"
+ " 'control': { 'run-oob': true } }");
+ g_assert(qdict_haskey(resp, "error"));
+ QDECREF(resp);
+
+ /*
+ * First send the "x-oob-test" command with lock=true and
+ * oob=false, it should hang the dispatcher and main thread;
+ * later, we send another lock=false with oob=true to continue
+ * that thread processing. Finally we should receive replies from
+ * both commands.
+ */
+ qmp_async("{ 'execute': 'x-oob-test',"
+ " 'arguments': { 'lock': true }, "
+ " 'id': 'lock-cmd'}");
+ qmp_async("{ 'execute': 'x-oob-test', "
+ " 'arguments': { 'lock': false }, "
+ " 'control': { 'run-oob': true }, "
+ " 'id': 'unlock-cmd' }");
+
+ /* Ignore all events. Wait for 2 acks */
+ while (acks < 2) {
+ resp = qmp_receive();
+ cmd_id = qdict_get_str(resp, "id");
+ if (!g_strcmp0(cmd_id, "lock-cmd") ||
+ !g_strcmp0(cmd_id, "unlock-cmd")) {
+ acks++;
+ }
+ QDECREF(resp);
+ }
+
+ qtest_end();
+}
+
static int query_error_class(const char *cmd)
{
static struct {
@@ -204,6 +298,11 @@ static bool query_is_blacklisted(const char *cmd)
"query-gic-capabilities", /* arm */
/* Success depends on target-specific build configuration: */
"query-pci", /* CONFIG_PCI */
+ /* Success depends on launching SEV guest */
+ "query-sev-launch-measure",
+ /* Success depends on Host or Hypervisor SEV support */
+ "query-sev",
+ "query-sev-capabilities",
NULL
};
int i;
@@ -313,6 +412,7 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
qtest_add_func("qmp/protocol", test_qmp_protocol);
+ qtest_add_func("qmp/oob", test_qmp_oob);
qmp_schema_init(&schema);
add_query_tests(&schema);
diff --git a/tests/qom-test.c b/tests/qom-test.c
index 9dab7ac61e..a34ff6ba53 100644
--- a/tests/qom-test.c
+++ b/tests/qom-test.c
@@ -62,9 +62,9 @@ static void test_properties(const char *path, bool recurse)
}
g_assert(qdict_haskey(response, "return"));
- list = qobject_to_qlist(qdict_get(response, "return"));
+ list = qobject_to(QList, qdict_get(response, "return"));
QLIST_FOREACH_ENTRY(list, entry) {
- tuple = qobject_to_qdict(qlist_entry_obj(entry));
+ tuple = qobject_to(QDict, qlist_entry_obj(entry));
bool is_child = strstart(qdict_get_str(tuple, "type"), "child<", NULL);
bool is_link = strstart(qdict_get_str(tuple, "type"), "link<", NULL);
diff --git a/tests/rcutorture.c b/tests/rcutorture.c
index 4002ecf123..49311c82ea 100644
--- a/tests/rcutorture.c
+++ b/tests/rcutorture.c
@@ -238,7 +238,6 @@ long long rcu_stress_count[RCU_STRESS_PIPE_LEN + 1];
static void *rcu_read_stress_test(void *arg)
{
int i;
- int itercnt = 0;
struct rcu_stress *p;
int pc;
long long n_reads_local = 0;
@@ -269,9 +268,6 @@ static void *rcu_read_stress_test(void *arg)
}
rcu_stress_local[pc]++;
n_reads_local++;
- if ((++itercnt % 0x1000) == 0) {
- synchronize_rcu();
- }
}
qemu_mutex_lock(&counts_mutex);
n_reads += n_reads_local;
diff --git a/tests/socket-helpers.c b/tests/socket-helpers.c
new file mode 100644
index 0000000000..8112763f5b
--- /dev/null
+++ b/tests/socket-helpers.c
@@ -0,0 +1,149 @@
+/*
+ * Helper functions for tests using sockets
+ *
+ * Copyright 2015-2018 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 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/sockets.h"
+#include "socket-helpers.h"
+
+#ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+#endif
+#ifndef EAI_ADDRFAMILY
+# define EAI_ADDRFAMILY 0
+#endif
+
+int socket_can_bind_connect(const char *hostname)
+{
+ int lfd = -1, cfd = -1, afd = -1;
+ struct addrinfo ai, *res = NULL;
+ struct sockaddr_storage ss;
+ socklen_t sslen = sizeof(ss);
+ int soerr;
+ socklen_t soerrlen = sizeof(soerr);
+ bool check_soerr = false;
+ int rc;
+ int ret = -1;
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+ ai.ai_family = AF_UNSPEC;
+ ai.ai_socktype = SOCK_STREAM;
+
+ /* lookup */
+ rc = getaddrinfo(hostname, NULL, &ai, &res);
+ if (rc != 0) {
+ if (rc == EAI_ADDRFAMILY ||
+ rc == EAI_FAMILY) {
+ errno = EADDRNOTAVAIL;
+ } else {
+ errno = EINVAL;
+ }
+ goto cleanup;
+ }
+
+ lfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (lfd < 0) {
+ goto cleanup;
+ }
+
+ cfd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (cfd < 0) {
+ goto cleanup;
+ }
+
+ if (bind(lfd, res->ai_addr, res->ai_addrlen) < 0) {
+ goto cleanup;
+ }
+
+ if (listen(lfd, 1) < 0) {
+ goto cleanup;
+ }
+
+ if (getsockname(lfd, (struct sockaddr *)&ss, &sslen) < 0) {
+ goto cleanup;
+ }
+
+ qemu_set_nonblock(cfd);
+ if (connect(cfd, (struct sockaddr *)&ss, sslen) < 0) {
+ if (errno == EINPROGRESS) {
+ check_soerr = true;
+ } else {
+ goto cleanup;
+ }
+ }
+
+ sslen = sizeof(ss);
+ afd = accept(lfd, (struct sockaddr *)&ss, &sslen);
+ if (afd < 0) {
+ goto cleanup;
+ }
+
+ if (check_soerr) {
+ if (qemu_getsockopt(cfd, SOL_SOCKET, SO_ERROR, &soerr, &soerrlen) < 0) {
+ goto cleanup;
+ }
+ if (soerr) {
+ errno = soerr;
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (afd != -1) {
+ close(afd);
+ }
+ if (cfd != -1) {
+ close(cfd);
+ }
+ if (lfd != -1) {
+ close(lfd);
+ }
+ if (res) {
+ freeaddrinfo(res);
+ }
+ return ret;
+}
+
+
+int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6)
+{
+ *has_ipv4 = *has_ipv6 = false;
+
+ if (socket_can_bind_connect("127.0.0.1") < 0) {
+ if (errno != EADDRNOTAVAIL) {
+ return -1;
+ }
+ } else {
+ *has_ipv4 = true;
+ }
+
+ if (socket_can_bind_connect("::1") < 0) {
+ if (errno != EADDRNOTAVAIL) {
+ return -1;
+ }
+ } else {
+ *has_ipv6 = true;
+ }
+
+ return 0;
+}
diff --git a/tests/socket-helpers.h b/tests/socket-helpers.h
new file mode 100644
index 0000000000..1c07d6d656
--- /dev/null
+++ b/tests/socket-helpers.h
@@ -0,0 +1,42 @@
+/*
+ * Helper functions for tests using sockets
+ *
+ * Copyright 2015-2018 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 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * @hostname: a DNS name or numeric IP address
+ *
+ * Check whether it is possible to bind & connect to ports
+ * on the DNS name or IP address @hostname. If an IP address
+ * is used, it must not be a wildcard address.
+ *
+ * Returns 0 on success, -1 on error with errno set
+ */
+int socket_can_bind_connect(const char *hostname);
+
+/*
+ * @has_ipv4: set to true on return if IPv4 is available
+ * @has_ipv6: set to true on return if IPv6 is available
+ *
+ * Check whether IPv4 and/or IPv6 are available for use.
+ * On success, @has_ipv4 and @has_ipv6 will be set to
+ * indicate whether the respective protocols are available.
+ *
+ * Returns 0 on success, -1 on fatal error
+ */
+int socket_check_protocol_support(bool *has_ipv4, bool *has_ipv6);
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
index d760e2b243..7673de1062 100644
--- a/tests/test-bdrv-drain.c
+++ b/tests/test-bdrv-drain.c
@@ -505,6 +505,7 @@ static void coroutine_fn test_job_start(void *opaque)
{
TestBlockJob *s = opaque;
+ block_job_event_ready(&s->common);
while (!s->should_complete) {
block_job_sleep_ns(&s->common, 100000);
}
@@ -541,8 +542,8 @@ static void test_blockjob_common(enum drain_type drain_type)
blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
blk_insert_bs(blk_target, target, &error_abort);
- job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
- 0, NULL, NULL, &error_abort);
+ job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL,
+ 0, 0, NULL, NULL, &error_abort);
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
block_job_start(job);
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
index 3591c9617f..5789893dda 100644
--- a/tests/test-blockjob-txn.c
+++ b/tests/test-blockjob-txn.c
@@ -87,7 +87,7 @@ static const BlockJobDriver test_block_job_driver = {
*/
static BlockJob *test_block_job_start(unsigned int iterations,
bool use_timer,
- int rc, int *result)
+ int rc, int *result, BlockJobTxn *txn)
{
BlockDriverState *bs;
TestBlockJob *s;
@@ -101,7 +101,7 @@ static BlockJob *test_block_job_start(unsigned int iterations,
g_assert_nonnull(bs);
snprintf(job_id, sizeof(job_id), "job%u", counter++);
- s = block_job_create(job_id, &test_block_job_driver, bs,
+ s = block_job_create(job_id, &test_block_job_driver, txn, bs,
0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
test_block_job_cb, data, &error_abort);
s->iterations = iterations;
@@ -120,12 +120,11 @@ static void test_single_job(int expected)
int result = -EINPROGRESS;
txn = block_job_txn_new();
- job = test_block_job_start(1, true, expected, &result);
- block_job_txn_add_job(txn, job);
+ job = test_block_job_start(1, true, expected, &result, txn);
block_job_start(job);
if (expected == -ECANCELED) {
- block_job_cancel(job);
+ block_job_cancel(job, false);
}
while (result == -EINPROGRESS) {
@@ -160,10 +159,8 @@ static void test_pair_jobs(int expected1, int expected2)
int result2 = -EINPROGRESS;
txn = block_job_txn_new();
- job1 = test_block_job_start(1, true, expected1, &result1);
- block_job_txn_add_job(txn, job1);
- job2 = test_block_job_start(2, true, expected2, &result2);
- block_job_txn_add_job(txn, job2);
+ job1 = test_block_job_start(1, true, expected1, &result1, txn);
+ job2 = test_block_job_start(2, true, expected2, &result2, txn);
block_job_start(job1);
block_job_start(job2);
@@ -173,10 +170,10 @@ static void test_pair_jobs(int expected1, int expected2)
block_job_txn_unref(txn);
if (expected1 == -ECANCELED) {
- block_job_cancel(job1);
+ block_job_cancel(job1, false);
}
if (expected2 == -ECANCELED) {
- block_job_cancel(job2);
+ block_job_cancel(job2, false);
}
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
@@ -224,14 +221,12 @@ static void test_pair_jobs_fail_cancel_race(void)
int result2 = -EINPROGRESS;
txn = block_job_txn_new();
- job1 = test_block_job_start(1, true, -ECANCELED, &result1);
- block_job_txn_add_job(txn, job1);
- job2 = test_block_job_start(2, false, 0, &result2);
- block_job_txn_add_job(txn, job2);
+ job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
+ job2 = test_block_job_start(2, false, 0, &result2, txn);
block_job_start(job1);
block_job_start(job2);
- block_job_cancel(job1);
+ block_job_cancel(job1, false);
/* Now make job2 finish before the main loop kicks jobs. This simulates
* the race between a pending kick and another job completing.
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
index 23bdf1a932..8946bfd37b 100644
--- a/tests/test-blockjob.c
+++ b/tests/test-blockjob.c
@@ -24,14 +24,15 @@ static void block_job_cb(void *opaque, int ret)
{
}
-static BlockJob *do_test_id(BlockBackend *blk, const char *id,
- bool should_succeed)
+static BlockJob *mk_job(BlockBackend *blk, const char *id,
+ const BlockJobDriver *drv, bool should_succeed,
+ int flags)
{
BlockJob *job;
Error *errp = NULL;
- job = block_job_create(id, &test_block_job_driver, blk_bs(blk),
- 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb,
+ job = block_job_create(id, drv, NULL, blk_bs(blk),
+ 0, BLK_PERM_ALL, 0, flags, block_job_cb,
NULL, &errp);
if (should_succeed) {
g_assert_null(errp);
@@ -50,6 +51,13 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
return job;
}
+static BlockJob *do_test_id(BlockBackend *blk, const char *id,
+ bool should_succeed)
+{
+ return mk_job(blk, id, &test_block_job_driver,
+ should_succeed, BLOCK_JOB_DEFAULT);
+}
+
/* This creates a BlockBackend (optionally with a name) with a
* BlockDriverState inserted. */
static BlockBackend *create_blk(const char *name)
@@ -142,6 +150,216 @@ static void test_job_ids(void)
destroy_blk(blk[2]);
}
+typedef struct CancelJob {
+ BlockJob common;
+ BlockBackend *blk;
+ bool should_converge;
+ bool should_complete;
+ bool completed;
+} CancelJob;
+
+static void cancel_job_completed(BlockJob *job, void *opaque)
+{
+ CancelJob *s = opaque;
+ s->completed = true;
+ block_job_completed(job, 0);
+}
+
+static void cancel_job_complete(BlockJob *job, Error **errp)
+{
+ CancelJob *s = container_of(job, CancelJob, common);
+ s->should_complete = true;
+}
+
+static void coroutine_fn cancel_job_start(void *opaque)
+{
+ CancelJob *s = opaque;
+
+ while (!s->should_complete) {
+ if (block_job_is_cancelled(&s->common)) {
+ goto defer;
+ }
+
+ if (!s->common.ready && s->should_converge) {
+ block_job_event_ready(&s->common);
+ }
+
+ block_job_sleep_ns(&s->common, 100000);
+ }
+
+ defer:
+ block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
+}
+
+static const BlockJobDriver test_cancel_driver = {
+ .instance_size = sizeof(CancelJob),
+ .start = cancel_job_start,
+ .complete = cancel_job_complete,
+};
+
+static CancelJob *create_common(BlockJob **pjob)
+{
+ BlockBackend *blk;
+ BlockJob *job;
+ CancelJob *s;
+
+ blk = create_blk(NULL);
+ job = mk_job(blk, "Steve", &test_cancel_driver, true,
+ BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
+ block_job_ref(job);
+ assert(job->status == BLOCK_JOB_STATUS_CREATED);
+ s = container_of(job, CancelJob, common);
+ s->blk = blk;
+
+ *pjob = job;
+ return s;
+}
+
+static void cancel_common(CancelJob *s)
+{
+ BlockJob *job = &s->common;
+ BlockBackend *blk = s->blk;
+ BlockJobStatus sts = job->status;
+
+ block_job_cancel_sync(job);
+ if ((sts != BLOCK_JOB_STATUS_CREATED) &&
+ (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
+ BlockJob *dummy = job;
+ block_job_dismiss(&dummy, &error_abort);
+ }
+ assert(job->status == BLOCK_JOB_STATUS_NULL);
+ block_job_unref(job);
+ destroy_blk(blk);
+}
+
+static void test_cancel_created(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+ cancel_common(s);
+}
+
+static void test_cancel_running(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ cancel_common(s);
+}
+
+static void test_cancel_paused(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ block_job_user_pause(job, &error_abort);
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_PAUSED);
+
+ cancel_common(s);
+}
+
+static void test_cancel_ready(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ s->should_converge = true;
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_READY);
+
+ cancel_common(s);
+}
+
+static void test_cancel_standby(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ s->should_converge = true;
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_READY);
+
+ block_job_user_pause(job, &error_abort);
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_STANDBY);
+
+ cancel_common(s);
+}
+
+static void test_cancel_pending(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ s->should_converge = true;
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_READY);
+
+ block_job_complete(job, &error_abort);
+ block_job_enter(job);
+ while (!s->completed) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+ assert(job->status == BLOCK_JOB_STATUS_PENDING);
+
+ cancel_common(s);
+}
+
+static void test_cancel_concluded(void)
+{
+ BlockJob *job;
+ CancelJob *s;
+
+ s = create_common(&job);
+
+ block_job_start(job);
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
+
+ s->should_converge = true;
+ block_job_enter(job);
+ assert(job->status == BLOCK_JOB_STATUS_READY);
+
+ block_job_complete(job, &error_abort);
+ block_job_enter(job);
+ while (!s->completed) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
+ assert(job->status == BLOCK_JOB_STATUS_PENDING);
+
+ block_job_finalize(job, &error_abort);
+ assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
+
+ cancel_common(s);
+}
+
int main(int argc, char **argv)
{
qemu_init_main_loop(&error_abort);
@@ -149,5 +367,12 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
g_test_add_func("/blockjob/ids", test_job_ids);
+ g_test_add_func("/blockjob/cancel/created", test_cancel_created);
+ g_test_add_func("/blockjob/cancel/running", test_cancel_running);
+ g_test_add_func("/blockjob/cancel/paused", test_cancel_paused);
+ g_test_add_func("/blockjob/cancel/ready", test_cancel_ready);
+ g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
+ g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
+ g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
return g_test_run();
}
diff --git a/tests/test-char.c b/tests/test-char.c
index b3a77af085..306c728335 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -166,7 +166,6 @@ static void char_mux_test(void)
FeHandler h1 = { 0, }, h2 = { 0, };
CharBackend chr_be1, chr_be2;
- muxes_realized = true; /* done after machine init */
opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
1, &error_abort);
qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
@@ -302,9 +301,8 @@ static int socket_can_read_hello(void *opaque)
return 10;
}
-static void char_socket_test(void)
+static void char_socket_test_common(Chardev *chr)
{
- Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
Chardev *chr_client;
QObject *addr;
QDict *qdict;
@@ -321,7 +319,7 @@ static void char_socket_test(void)
g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort));
addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
- qdict = qobject_to_qdict(addr);
+ qdict = qobject_to(QDict, addr);
port = qdict_get_str(qdict, "port");
tmp = g_strdup_printf("tcp:127.0.0.1:%s", port);
QDECREF(qdict);
@@ -359,6 +357,47 @@ static void char_socket_test(void)
object_unparent(OBJECT(chr));
}
+
+static void char_socket_basic_test(void)
+{
+ Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait");
+
+ char_socket_test_common(chr);
+}
+
+
+static void char_socket_fdpass_test(void)
+{
+ Chardev *chr;
+ char *optstr;
+ QemuOpts *opts;
+ int fd;
+ SocketAddress *addr = g_new0(SocketAddress, 1);
+
+ addr->type = SOCKET_ADDRESS_TYPE_INET;
+ addr->u.inet.host = g_strdup("127.0.0.1");
+ addr->u.inet.port = g_strdup("0");
+
+ fd = socket_listen(addr, &error_abort);
+ g_assert(fd >= 0);
+
+ qapi_free_SocketAddress(addr);
+
+ optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd);
+
+ opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
+ optstr, true);
+ g_free(optstr);
+ g_assert_nonnull(opts);
+
+ chr = qemu_chr_new_from_opts(opts, &error_abort);
+
+ qemu_opts_del(opts);
+
+ char_socket_test_common(chr);
+}
+
+
#ifndef _WIN32
static void char_pipe_test(void)
{
@@ -775,7 +814,8 @@ int main(int argc, char **argv)
#ifndef _WIN32
g_test_add_func("/char/file-fifo", char_file_fifo_test);
#endif
- g_test_add_func("/char/socket", char_socket_test);
+ g_test_add_func("/char/socket/basic", char_socket_basic_test);
+ g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test);
g_test_add_func("/char/udp", char_udp_test);
#ifdef HAVE_CHARDEV_SERIAL
g_test_add_func("/char/serial", char_serial_test);
diff --git a/tests/test-cutils.c b/tests/test-cutils.c
index f64a49b7fb..64a489c2e9 100644
--- a/tests/test-cutils.c
+++ b/tests/test-cutils.c
@@ -223,6 +223,583 @@ static void test_parse_uint_full_correct(void)
g_assert_cmpint(i, ==, 123);
}
+static void test_qemu_strtoi_correct(void)
+{
+ const char *str = "12345 foo";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 12345);
+ g_assert(endptr == str + 5);
+}
+
+static void test_qemu_strtoi_null(void)
+{
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(NULL, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == NULL);
+}
+
+static void test_qemu_strtoi_empty(void)
+{
+ const char *str = "";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoi_whitespace(void)
+{
+ const char *str = " \t ";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoi_invalid(void)
+{
+ const char *str = " xxxx \t abc";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoi_trailing(void)
+{
+ const char *str = "123xxx";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 123);
+ g_assert(endptr == str + 3);
+}
+
+static void test_qemu_strtoi_octal(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 8, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0123);
+ g_assert(endptr == str + strlen(str));
+
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoi_decimal(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 10, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 123);
+ g_assert(endptr == str + strlen(str));
+
+ str = "123";
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoi_hex(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 16, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0x123);
+ g_assert(endptr == str + strlen(str));
+
+ str = "0x123";
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 0x123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoi_max(void)
+{
+ char *str = g_strdup_printf("%d", INT_MAX);
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, INT_MAX);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoi_overflow(void)
+{
+ char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll);
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -ERANGE);
+ g_assert_cmpint(res, ==, INT_MAX);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoi_underflow(void)
+{
+ char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll);
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -ERANGE);
+ g_assert_cmpint(res, ==, INT_MIN);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoi_negative(void)
+{
+ const char *str = " \t -321";
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, -321);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoi_full_correct(void)
+{
+ const char *str = "123";
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, 123);
+}
+
+static void test_qemu_strtoi_full_null(void)
+{
+ char f = 'X';
+ const char *endptr = &f;
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(NULL, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == NULL);
+}
+
+static void test_qemu_strtoi_full_empty(void)
+{
+ const char *str = "";
+ int res = 999L;
+ int err;
+
+ err = qemu_strtoi(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+}
+
+static void test_qemu_strtoi_full_negative(void)
+{
+ const char *str = " \t -321";
+ int res = 999;
+ int err;
+
+ err = qemu_strtoi(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, -321);
+}
+
+static void test_qemu_strtoi_full_trailing(void)
+{
+ const char *str = "123xxx";
+ int res;
+ int err;
+
+ err = qemu_strtoi(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+}
+
+static void test_qemu_strtoi_full_max(void)
+{
+ char *str = g_strdup_printf("%d", INT_MAX);
+ int res;
+ int err;
+
+ err = qemu_strtoi(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpint(res, ==, INT_MAX);
+ g_free(str);
+}
+
+static void test_qemu_strtoui_correct(void)
+{
+ const char *str = "12345 foo";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 12345);
+ g_assert(endptr == str + 5);
+}
+
+static void test_qemu_strtoui_null(void)
+{
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(NULL, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == NULL);
+}
+
+static void test_qemu_strtoui_empty(void)
+{
+ const char *str = "";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoui_whitespace(void)
+{
+ const char *str = " \t ";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoui_invalid(void)
+{
+ const char *str = " xxxx \t abc";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+ g_assert(endptr == str);
+}
+
+static void test_qemu_strtoui_trailing(void)
+{
+ const char *str = "123xxx";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 123);
+ g_assert(endptr == str + 3);
+}
+
+static void test_qemu_strtoui_octal(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 8, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 0123);
+ g_assert(endptr == str + strlen(str));
+
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 0123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoui_decimal(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 10, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 123);
+ g_assert(endptr == str + strlen(str));
+
+ str = "123";
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoui_hex(void)
+{
+ const char *str = "0123";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 16, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmphex(res, ==, 0x123);
+ g_assert(endptr == str + strlen(str));
+
+ str = "0x123";
+ res = 999;
+ endptr = &f;
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmphex(res, ==, 0x123);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoui_max(void)
+{
+ char *str = g_strdup_printf("%u", UINT_MAX);
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmphex(res, ==, UINT_MAX);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoui_overflow(void)
+{
+ char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll);
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -ERANGE);
+ g_assert_cmphex(res, ==, UINT_MAX);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoui_underflow(void)
+{
+ char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll);
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, -ERANGE);
+ g_assert_cmpuint(res, ==, (unsigned int)-1);
+ g_assert(endptr == str + strlen(str));
+ g_free(str);
+}
+
+static void test_qemu_strtoui_negative(void)
+{
+ const char *str = " \t -321";
+ char f = 'X';
+ const char *endptr = &f;
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, &endptr, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, (unsigned int)-321);
+ g_assert(endptr == str + strlen(str));
+}
+
+static void test_qemu_strtoui_full_correct(void)
+{
+ const char *str = "123";
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, 123);
+}
+
+static void test_qemu_strtoui_full_null(void)
+{
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(NULL, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+}
+
+static void test_qemu_strtoui_full_empty(void)
+{
+ const char *str = "";
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+}
+static void test_qemu_strtoui_full_negative(void)
+{
+ const char *str = " \t -321";
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, NULL, 0, &res);
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmpuint(res, ==, (unsigned int)-321);
+}
+
+static void test_qemu_strtoui_full_trailing(void)
+{
+ const char *str = "123xxx";
+ unsigned int res;
+ int err;
+
+ err = qemu_strtoui(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, -EINVAL);
+}
+
+static void test_qemu_strtoui_full_max(void)
+{
+ char *str = g_strdup_printf("%u", UINT_MAX);
+ unsigned int res = 999;
+ int err;
+
+ err = qemu_strtoui(str, NULL, 0, &res);
+
+ g_assert_cmpint(err, ==, 0);
+ g_assert_cmphex(res, ==, UINT_MAX);
+ g_free(str);
+}
+
static void test_qemu_strtol_correct(void)
{
const char *str = "12345 foo";
@@ -1612,6 +2189,86 @@ int main(int argc, char **argv)
g_test_add_func("/cutils/parse_uint_full/correct",
test_parse_uint_full_correct);
+ /* qemu_strtoi() tests */
+ g_test_add_func("/cutils/qemu_strtoi/correct",
+ test_qemu_strtoi_correct);
+ g_test_add_func("/cutils/qemu_strtoi/null",
+ test_qemu_strtoi_null);
+ g_test_add_func("/cutils/qemu_strtoi/empty",
+ test_qemu_strtoi_empty);
+ g_test_add_func("/cutils/qemu_strtoi/whitespace",
+ test_qemu_strtoi_whitespace);
+ g_test_add_func("/cutils/qemu_strtoi/invalid",
+ test_qemu_strtoi_invalid);
+ g_test_add_func("/cutils/qemu_strtoi/trailing",
+ test_qemu_strtoi_trailing);
+ g_test_add_func("/cutils/qemu_strtoi/octal",
+ test_qemu_strtoi_octal);
+ g_test_add_func("/cutils/qemu_strtoi/decimal",
+ test_qemu_strtoi_decimal);
+ g_test_add_func("/cutils/qemu_strtoi/hex",
+ test_qemu_strtoi_hex);
+ g_test_add_func("/cutils/qemu_strtoi/max",
+ test_qemu_strtoi_max);
+ g_test_add_func("/cutils/qemu_strtoi/overflow",
+ test_qemu_strtoi_overflow);
+ g_test_add_func("/cutils/qemu_strtoi/underflow",
+ test_qemu_strtoi_underflow);
+ g_test_add_func("/cutils/qemu_strtoi/negative",
+ test_qemu_strtoi_negative);
+ g_test_add_func("/cutils/qemu_strtoi_full/correct",
+ test_qemu_strtoi_full_correct);
+ g_test_add_func("/cutils/qemu_strtoi_full/null",
+ test_qemu_strtoi_full_null);
+ g_test_add_func("/cutils/qemu_strtoi_full/empty",
+ test_qemu_strtoi_full_empty);
+ g_test_add_func("/cutils/qemu_strtoi_full/negative",
+ test_qemu_strtoi_full_negative);
+ g_test_add_func("/cutils/qemu_strtoi_full/trailing",
+ test_qemu_strtoi_full_trailing);
+ g_test_add_func("/cutils/qemu_strtoi_full/max",
+ test_qemu_strtoi_full_max);
+
+ /* qemu_strtoui() tests */
+ g_test_add_func("/cutils/qemu_strtoui/correct",
+ test_qemu_strtoui_correct);
+ g_test_add_func("/cutils/qemu_strtoui/null",
+ test_qemu_strtoui_null);
+ g_test_add_func("/cutils/qemu_strtoui/empty",
+ test_qemu_strtoui_empty);
+ g_test_add_func("/cutils/qemu_strtoui/whitespace",
+ test_qemu_strtoui_whitespace);
+ g_test_add_func("/cutils/qemu_strtoui/invalid",
+ test_qemu_strtoui_invalid);
+ g_test_add_func("/cutils/qemu_strtoui/trailing",
+ test_qemu_strtoui_trailing);
+ g_test_add_func("/cutils/qemu_strtoui/octal",
+ test_qemu_strtoui_octal);
+ g_test_add_func("/cutils/qemu_strtoui/decimal",
+ test_qemu_strtoui_decimal);
+ g_test_add_func("/cutils/qemu_strtoui/hex",
+ test_qemu_strtoui_hex);
+ g_test_add_func("/cutils/qemu_strtoui/max",
+ test_qemu_strtoui_max);
+ g_test_add_func("/cutils/qemu_strtoui/overflow",
+ test_qemu_strtoui_overflow);
+ g_test_add_func("/cutils/qemu_strtoui/underflow",
+ test_qemu_strtoui_underflow);
+ g_test_add_func("/cutils/qemu_strtoui/negative",
+ test_qemu_strtoui_negative);
+ g_test_add_func("/cutils/qemu_strtoui_full/correct",
+ test_qemu_strtoui_full_correct);
+ g_test_add_func("/cutils/qemu_strtoui_full/null",
+ test_qemu_strtoui_full_null);
+ g_test_add_func("/cutils/qemu_strtoui_full/empty",
+ test_qemu_strtoui_full_empty);
+ g_test_add_func("/cutils/qemu_strtoui_full/negative",
+ test_qemu_strtoui_full_negative);
+ g_test_add_func("/cutils/qemu_strtoui_full/trailing",
+ test_qemu_strtoui_full_trailing);
+ g_test_add_func("/cutils/qemu_strtoui_full/max",
+ test_qemu_strtoui_full_max);
+
/* qemu_strtol() tests */
g_test_add_func("/cutils/qemu_strtol/correct",
test_qemu_strtol_correct);
diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
index b273fd3ba2..0597213f93 100644
--- a/tests/test-io-channel-socket.c
+++ b/tests/test-io-channel-socket.c
@@ -22,77 +22,9 @@
#include "io/channel-socket.h"
#include "io/channel-util.h"
#include "io-channel-helpers.h"
+#include "socket-helpers.h"
#include "qapi/error.h"
-#ifndef AI_ADDRCONFIG
-# define AI_ADDRCONFIG 0
-#endif
-#ifndef EAI_ADDRFAMILY
-# define EAI_ADDRFAMILY 0
-#endif
-
-static int check_bind(const char *hostname, bool *has_proto)
-{
- int fd = -1;
- struct addrinfo ai, *res = NULL;
- int rc;
- int ret = -1;
-
- memset(&ai, 0, sizeof(ai));
- ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
- ai.ai_family = AF_UNSPEC;
- ai.ai_socktype = SOCK_STREAM;
-
- /* lookup */
- rc = getaddrinfo(hostname, NULL, &ai, &res);
- if (rc != 0) {
- if (rc == EAI_ADDRFAMILY ||
- rc == EAI_FAMILY) {
- *has_proto = false;
- goto done;
- }
- goto cleanup;
- }
-
- fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if (fd < 0) {
- goto cleanup;
- }
-
- if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
- if (errno == EADDRNOTAVAIL) {
- *has_proto = false;
- goto done;
- }
- goto cleanup;
- }
-
- *has_proto = true;
- done:
- ret = 0;
-
- cleanup:
- if (fd != -1) {
- close(fd);
- }
- if (res) {
- freeaddrinfo(res);
- }
- return ret;
-}
-
-static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
-{
- if (check_bind("127.0.0.1", has_ipv4) < 0) {
- return -1;
- }
- if (check_bind("::1", has_ipv6) < 0) {
- return -1;
- }
-
- return 0;
-}
-
static void test_io_channel_set_socket_bufs(QIOChannel *src,
QIOChannel *dst)
@@ -566,7 +498,7 @@ int main(int argc, char **argv)
* each protocol to avoid breaking tests on machines
* with either IPv4 or IPv6 disabled.
*/
- if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
+ if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
return 1;
}
diff --git a/tests/test-keyval.c b/tests/test-keyval.c
index 94eb4df28d..029f05202a 100644
--- a/tests/test-keyval.c
+++ b/tests/test-keyval.c
@@ -195,7 +195,7 @@ static void check_list012(QList *qlist)
g_assert(qlist);
for (i = 0; i < ARRAY_SIZE(expected); i++) {
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]);
QDECREF(qstr);
@@ -654,12 +654,12 @@ static void test_keyval_visit_any(void)
QDECREF(qdict);
visit_start_struct(v, NULL, NULL, 0, &error_abort);
visit_type_any(v, "a", &any, &error_abort);
- qlist = qobject_to_qlist(any);
+ qlist = qobject_to(QList, any);
g_assert(qlist);
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert_cmpstr(qstring_get_str(qstr), ==, "null");
QDECREF(qstr);
- qstr = qobject_to_qstring(qlist_pop(qlist));
+ qstr = qobject_to(QString, qlist_pop(qlist));
g_assert_cmpstr(qstring_get_str(qstr), ==, "1");
g_assert(qlist_empty(qlist));
QDECREF(qstr);
diff --git a/tests/test-qga.c b/tests/test-qga.c
index 5c5b661f8a..e6ab788f31 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -297,8 +297,8 @@ static void test_qga_get_vcpus(gconstpointer fix)
/* check there is at least a cpu */
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "logical-id"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id"));
QDECREF(ret);
}
@@ -318,10 +318,10 @@ static void test_qga_get_fsinfo(gconstpointer fix)
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
if (entry) {
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "mountpoint"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "type"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "disk"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk"));
}
QDECREF(ret);
@@ -363,8 +363,9 @@ static void test_qga_get_memory_blocks(gconstpointer fix)
entry = qlist_first(list);
/* newer versions of qga may return empty list without error */
if (entry) {
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "phys-index"));
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "online"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value),
+ "phys-index"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
}
}
@@ -385,7 +386,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix)
/* check there is at least an interface */
list = qdict_get_qlist(ret, "return");
entry = qlist_first(list);
- g_assert(qdict_haskey(qobject_to_qdict(entry->value), "name"));
+ g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
QDECREF(ret);
}
diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c
index 5b1cee6912..93fbbb1b73 100644
--- a/tests/test-qmp-cmds.c
+++ b/tests/test-qmp-cmds.c
@@ -100,7 +100,7 @@ static void test_dispatch_cmd(void)
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(!qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
@@ -117,7 +117,7 @@ static void test_dispatch_cmd_failure(void)
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
@@ -131,7 +131,7 @@ static void test_dispatch_cmd_failure(void)
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp != NULL);
- assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+ assert(qdict_haskey(qobject_to(QDict, resp), "error"));
qobject_decref(resp);
QDECREF(req);
@@ -145,7 +145,7 @@ static QObject *test_qmp_dispatch(QDict *req)
resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp_obj);
- resp = qobject_to_qdict(resp_obj);
+ resp = qobject_to(QDict, resp_obj);
assert(resp && !qdict_haskey(resp, "error"));
ret = qdict_get(resp, "return");
assert(ret);
@@ -176,7 +176,7 @@ static void test_dispatch_cmd_io(void)
qdict_put(req, "arguments", args);
qdict_put_str(req, "execute", "user_def_cmd2");
- ret = qobject_to_qdict(test_qmp_dispatch(req));
+ ret = qobject_to(QDict, test_qmp_dispatch(req));
assert(!strcmp(qdict_get_str(ret, "string0"), "blah1"));
ret_dict = qdict_get_qdict(ret, "dict1");
@@ -197,7 +197,7 @@ static void test_dispatch_cmd_io(void)
qdict_put(req, "arguments", args3);
qdict_put_str(req, "execute", "guest-get-time");
- ret3 = qobject_to_qnum(test_qmp_dispatch(req));
+ ret3 = qobject_to(QNum, test_qmp_dispatch(req));
g_assert(qnum_get_try_int(ret3, &val));
g_assert_cmpint(val, ==, 66);
QDECREF(ret3);
diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c
index 31f35b3e66..bb1036615f 100644
--- a/tests/test-qmp-event.c
+++ b/tests/test-qmp-event.c
@@ -60,22 +60,22 @@ void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque)
switch (qobject_type(obj1)) {
case QTYPE_QBOOL:
- d->result = (qbool_get_bool(qobject_to_qbool(obj1)) ==
- qbool_get_bool(qobject_to_qbool(obj2)));
+ d->result = (qbool_get_bool(qobject_to(QBool, obj1)) ==
+ qbool_get_bool(qobject_to(QBool, obj2)));
return;
case QTYPE_QNUM:
- g_assert(qnum_get_try_int(qobject_to_qnum(obj1), &val1));
- g_assert(qnum_get_try_int(qobject_to_qnum(obj2), &val2));
+ g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1));
+ g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2));
d->result = val1 == val2;
return;
case QTYPE_QSTRING:
- d->result = g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)),
- qstring_get_str(qobject_to_qstring(obj2))) == 0;
+ d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)),
+ qstring_get_str(qobject_to(QString, obj2))) == 0;
return;
case QTYPE_QDICT:
- d_new.expect = qobject_to_qdict(obj2);
+ d_new.expect = qobject_to(QDict, obj2);
d_new.result = true;
- qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new);
+ qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new);
d->result = d_new.result;
return;
default:
diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c
index 79b1a8cb17..6dc59c6211 100644
--- a/tests/test-qobject-input-visitor.c
+++ b/tests/test-qobject-input-visitor.c
@@ -479,7 +479,7 @@ static void test_visitor_in_any(TestInputVisitorData *data,
v = visitor_input_test_init(data, "-42");
visit_type_any(v, NULL, &res, &error_abort);
- qnum = qobject_to_qnum(res);
+ qnum = qobject_to(QNum, res);
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
@@ -487,22 +487,22 @@ static void test_visitor_in_any(TestInputVisitorData *data,
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
visit_type_any(v, NULL, &res, &error_abort);
- qdict = qobject_to_qdict(res);
+ qdict = qobject_to(QDict, res);
g_assert(qdict && qdict_size(qdict) == 3);
qobj = qdict_get(qdict, "integer");
g_assert(qobj);
- qnum = qobject_to_qnum(qobj);
+ qnum = qobject_to(QNum, qobj);
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
qobj = qdict_get(qdict, "boolean");
g_assert(qobj);
- qbool = qobject_to_qbool(qobj);
+ qbool = qobject_to(QBool, qobj);
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
qobj = qdict_get(qdict, "string");
g_assert(qobj);
- qstring = qobject_to_qstring(qobj);
+ qstring = qobject_to(QString, qobj);
g_assert(qstring);
g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
qobject_decref(res);
@@ -1250,24 +1250,27 @@ static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
}
static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
- const char *schema_json)
+ const QLitObject *qlit)
{
SchemaInfoList *schema = NULL;
+ QObject *obj = qobject_from_qlit(qlit);
Visitor *v;
- v = visitor_input_test_init_raw(data, schema_json);
+ v = qobject_input_visitor_new(obj);
visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
g_assert(schema);
qapi_free_SchemaInfoList(schema);
+ qobject_decref(obj);
+ visit_free(v);
}
static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
const void *unused)
{
- do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
- do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
+ do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit);
+ do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit);
}
int main(int argc, char **argv)
diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c
index 11e8c5aa40..ecf21c0f31 100644
--- a/tests/test-qobject-output-visitor.c
+++ b/tests/test-qobject-output-visitor.c
@@ -66,7 +66,7 @@ static void test_visitor_out_int(TestOutputVisitorData *data,
visit_type_int(data->ov, NULL, &value, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, value);
@@ -80,7 +80,7 @@ static void test_visitor_out_bool(TestOutputVisitorData *data,
visit_type_bool(data->ov, NULL, &value, &error_abort);
- qbool = qobject_to_qbool(visitor_get(data));
+ qbool = qobject_to(QBool, visitor_get(data));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == value);
}
@@ -93,7 +93,7 @@ static void test_visitor_out_number(TestOutputVisitorData *data,
visit_type_number(data->ov, NULL, &value, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_double(qnum) == value);
}
@@ -106,7 +106,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data,
visit_type_str(data->ov, NULL, &string, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, string);
}
@@ -120,7 +120,7 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data,
/* A null string should return "" */
visit_type_str(data->ov, NULL, &string, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, "");
}
@@ -134,7 +134,7 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
for (i = 0; i < ENUM_ONE__MAX; i++) {
visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i));
visitor_reset(data);
@@ -167,7 +167,7 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
@@ -206,7 +206,7 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 2);
g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]);
@@ -280,7 +280,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
- qlist = qobject_to_qlist(visitor_get(data));
+ qlist = qobject_to(QList, visitor_get(data));
g_assert(qlist);
g_assert(!qlist_empty(qlist));
@@ -289,7 +289,7 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
QLIST_FOREACH_ENTRY(qlist, entry) {
QDict *qdict;
- qdict = qobject_to_qdict(entry->value);
+ qdict = qobject_to(QDict, entry->value);
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 3);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i);
@@ -342,7 +342,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
qobj = QOBJECT(qnum_from_int(-42));
visit_type_any(data->ov, NULL, &qobj, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
@@ -356,16 +356,16 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
qobj = QOBJECT(qdict);
visit_type_any(data->ov, NULL, &qobj, &error_abort);
qobject_decref(qobj);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
- qnum = qobject_to_qnum(qdict_get(qdict, "integer"));
+ qnum = qobject_to(QNum, qdict_get(qdict, "integer"));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, -42);
- qbool = qobject_to_qbool(qdict_get(qdict, "boolean"));
+ qbool = qobject_to(QBool, qdict_get(qdict, "boolean"));
g_assert(qbool);
g_assert(qbool_get_bool(qbool) == true);
- qstring = qobject_to_qstring(qdict_get(qdict, "string"));
+ qstring = qobject_to(QString, qdict_get(qdict, "string"));
g_assert(qstring);
g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
}
@@ -382,7 +382,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data,
tmp->u.value1.boolean = true;
visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
@@ -406,7 +406,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
tmp->u.i = 42;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qnum = qobject_to_qnum(visitor_get(data));
+ qnum = qobject_to(QNum, visitor_get(data));
g_assert(qnum);
g_assert(qnum_get_try_int(qnum, &val));
g_assert_cmpint(val, ==, 42);
@@ -419,7 +419,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
tmp->u.e = ENUM_ONE_VALUE1;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qstr = qobject_to_qstring(visitor_get(data));
+ qstr = qobject_to(QString, visitor_get(data));
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, "value1");
@@ -444,7 +444,7 @@ static void test_visitor_out_alternate(TestOutputVisitorData *data,
tmp->u.udfu.u.value1.boolean = true;
visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 4);
g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1);
@@ -466,7 +466,7 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
visit_type_null(data->ov, "a", &null, &error_abort);
visit_check_struct(data->ov, &error_abort);
visit_end_struct(data->ov, NULL);
- qdict = qobject_to_qdict(visitor_get(data));
+ qdict = qobject_to(QDict, visitor_get(data));
g_assert(qdict);
g_assert_cmpint(qdict_size(qdict), ==, 1);
nil = qdict_get(qdict, "a");
@@ -610,10 +610,10 @@ static void check_native_list(QObject *qobj,
QList *qlist;
int i;
- qdict = qobject_to_qdict(qobj);
+ qdict = qobject_to(QDict, qobj);
g_assert(qdict);
g_assert(qdict_haskey(qdict, "data"));
- qlist = qlist_copy(qobject_to_qlist(qdict_get(qdict, "data")));
+ qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data")));
switch (kind) {
case USER_DEF_NATIVE_LIST_UNION_KIND_U8:
@@ -627,7 +627,7 @@ static void check_native_list(QObject *qobj,
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_assert(qnum_get_try_uint(qvalue, &val));
g_assert_cmpint(val, ==, i);
qobject_decref(qlist_pop(qlist));
@@ -651,7 +651,7 @@ static void check_native_list(QObject *qobj,
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_assert(qnum_get_try_int(qvalue, &val));
g_assert_cmpint(val, ==, i);
qobject_decref(qlist_pop(qlist));
@@ -663,7 +663,7 @@ static void check_native_list(QObject *qobj,
QBool *qvalue;
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qbool(tmp);
+ qvalue = qobject_to(QBool, tmp);
g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0);
qobject_decref(qlist_pop(qlist));
}
@@ -675,7 +675,7 @@ static void check_native_list(QObject *qobj,
gchar str[8];
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qstring(tmp);
+ qvalue = qobject_to(QString, tmp);
sprintf(str, "%d", i);
g_assert_cmpstr(qstring_get_str(qvalue), ==, str);
qobject_decref(qlist_pop(qlist));
@@ -690,7 +690,7 @@ static void check_native_list(QObject *qobj,
tmp = qlist_peek(qlist);
g_assert(tmp);
- qvalue = qobject_to_qnum(tmp);
+ qvalue = qobject_to(QNum, tmp);
g_string_printf(double_expected, "%.6f", (double)i / 3);
g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue));
g_assert_cmpstr(double_actual->str, ==, double_expected->str);
diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c
new file mode 100644
index 0000000000..acadd85e8f
--- /dev/null
+++ b/tests/test-util-sockets.c
@@ -0,0 +1,266 @@
+/*
+ * Tests for util/qemu-sockets.c
+ *
+ * Copyright 2018 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 library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "socket-helpers.h"
+#include "monitor/monitor.h"
+
+static void test_fd_is_socket_bad(void)
+{
+ char *tmp = g_strdup("qemu-test-util-sockets-XXXXXX");
+ int fd = mkstemp(tmp);
+ if (fd != 0) {
+ unlink(tmp);
+ }
+ g_free(tmp);
+
+ g_assert(fd >= 0);
+
+ g_assert(!fd_is_socket(fd));
+ close(fd);
+}
+
+static void test_fd_is_socket_good(void)
+{
+ int fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+
+ g_assert(fd >= 0);
+
+ g_assert(fd_is_socket(fd));
+ close(fd);
+}
+
+static int mon_fd = -1;
+static const char *mon_fdname;
+
+int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
+{
+ g_assert(cur_mon);
+ g_assert(mon == cur_mon);
+ if (mon_fd == -1 || !g_str_equal(mon_fdname, fdname)) {
+ error_setg(errp, "No fd named %s", fdname);
+ return -1;
+ }
+ return dup(mon_fd);
+}
+
+/* Syms in libqemustub.a are discarded at .o file granularity.
+ * To replace monitor_get_fd() we must ensure everything in
+ * stubs/monitor.c is defined, to make sure monitor.o is discarded
+ * otherwise we get duplicate syms at link time.
+ */
+Monitor *cur_mon;
+void monitor_init(Chardev *chr, int flags) {}
+
+
+static void test_socket_fd_pass_name_good(void)
+{
+ SocketAddress addr;
+ int fd;
+
+ cur_mon = g_malloc(1); /* Fake a monitor */
+ mon_fdname = "myfd";
+ mon_fd = qemu_socket(AF_INET, SOCK_STREAM, 0);
+ g_assert_cmpint(mon_fd, >, STDERR_FILENO);
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup(mon_fdname);
+
+ fd = socket_connect(&addr, &error_abort);
+ g_assert_cmpint(fd, !=, -1);
+ g_assert_cmpint(fd, !=, mon_fd);
+ close(fd);
+
+ fd = socket_listen(&addr, &error_abort);
+ g_assert_cmpint(fd, !=, -1);
+ g_assert_cmpint(fd, !=, mon_fd);
+ close(fd);
+
+ g_free(addr.u.fd.str);
+ mon_fdname = NULL;
+ close(mon_fd);
+ mon_fd = -1;
+ g_free(cur_mon);
+ cur_mon = NULL;
+}
+
+static void test_socket_fd_pass_name_bad(void)
+{
+ SocketAddress addr;
+ Error *err = NULL;
+ int fd;
+
+ cur_mon = g_malloc(1); /* Fake a monitor */
+ mon_fdname = "myfd";
+ mon_fd = dup(STDOUT_FILENO);
+ g_assert_cmpint(mon_fd, >, STDERR_FILENO);
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup(mon_fdname);
+
+ fd = socket_connect(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ fd = socket_listen(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ g_free(addr.u.fd.str);
+ mon_fdname = NULL;
+ close(mon_fd);
+ mon_fd = -1;
+ g_free(cur_mon);
+ cur_mon = NULL;
+}
+
+static void test_socket_fd_pass_name_nomon(void)
+{
+ SocketAddress addr;
+ Error *err = NULL;
+ int fd;
+
+ g_assert(cur_mon == NULL);
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup("myfd");
+
+ fd = socket_connect(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ fd = socket_listen(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ g_free(addr.u.fd.str);
+}
+
+
+static void test_socket_fd_pass_num_good(void)
+{
+ SocketAddress addr;
+ int fd, sfd;
+
+ g_assert(cur_mon == NULL);
+ sfd = qemu_socket(AF_INET, SOCK_STREAM, 0);
+ g_assert_cmpint(sfd, >, STDERR_FILENO);
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup_printf("%d", sfd);
+
+ fd = socket_connect(&addr, &error_abort);
+ g_assert_cmpint(fd, ==, sfd);
+
+ fd = socket_listen(&addr, &error_abort);
+ g_assert_cmpint(fd, ==, sfd);
+
+ g_free(addr.u.fd.str);
+ close(sfd);
+}
+
+static void test_socket_fd_pass_num_bad(void)
+{
+ SocketAddress addr;
+ Error *err = NULL;
+ int fd, sfd;
+
+ g_assert(cur_mon == NULL);
+ sfd = dup(STDOUT_FILENO);
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup_printf("%d", sfd);
+
+ fd = socket_connect(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ fd = socket_listen(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ g_free(addr.u.fd.str);
+ close(sfd);
+}
+
+static void test_socket_fd_pass_num_nocli(void)
+{
+ SocketAddress addr;
+ Error *err = NULL;
+ int fd;
+
+ cur_mon = g_malloc(1); /* Fake a monitor */
+
+ addr.type = SOCKET_ADDRESS_TYPE_FD;
+ addr.u.fd.str = g_strdup_printf("%d", STDOUT_FILENO);
+
+ fd = socket_connect(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ fd = socket_listen(&addr, &err);
+ g_assert_cmpint(fd, ==, -1);
+ error_free_or_abort(&err);
+
+ g_free(addr.u.fd.str);
+}
+
+
+int main(int argc, char **argv)
+{
+ bool has_ipv4, has_ipv6;
+
+ socket_init();
+
+ g_test_init(&argc, &argv, NULL);
+
+ /* We're creating actual IPv4/6 sockets, so we should
+ * check if the host running tests actually supports
+ * each protocol to avoid breaking tests on machines
+ * with either IPv4 or IPv6 disabled.
+ */
+ if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
+ return 1;
+ }
+
+ if (has_ipv4) {
+ g_test_add_func("/util/socket/is-socket/bad",
+ test_fd_is_socket_bad);
+ g_test_add_func("/util/socket/is-socket/good",
+ test_fd_is_socket_good);
+ g_test_add_func("/socket/fd-pass/name/good",
+ test_socket_fd_pass_name_good);
+ g_test_add_func("/socket/fd-pass/name/bad",
+ test_socket_fd_pass_name_bad);
+ g_test_add_func("/socket/fd-pass/name/nomon",
+ test_socket_fd_pass_name_nomon);
+ g_test_add_func("/socket/fd-pass/num/good",
+ test_socket_fd_pass_num_good);
+ g_test_add_func("/socket/fd-pass/num/bad",
+ test_socket_fd_pass_num_bad);
+ g_test_add_func("/socket/fd-pass/num/nocli",
+ test_socket_fd_pass_num_nocli);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c
index 495dd1e7ef..02e41843fc 100644
--- a/tests/test-x86-cpuid-compat.c
+++ b/tests/test-x86-cpuid-compat.c
@@ -17,7 +17,7 @@ static char *get_cpu0_qom_path(void)
g_assert(qdict_haskey(resp, "return"));
ret = qdict_get_qlist(resp, "return");
- cpu0 = qobject_to_qdict(qlist_peek(ret));
+ cpu0 = qobject_to(QDict, qlist_peek(ret));
path = g_strdup(qdict_get_str(cpu0, "qom_path"));
QDECREF(resp);
return path;
@@ -38,7 +38,7 @@ static QObject *qom_get(const char *path, const char *prop)
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
static bool qom_get_bool(const char *path, const char *prop)
{
- QBool *value = qobject_to_qbool(qom_get(path, prop));
+ QBool *value = qobject_to(QBool, qom_get(path, prop));
bool b = qbool_get_bool(value);
QDECREF(value);
@@ -61,7 +61,7 @@ static void test_cpuid_prop(const void *data)
qtest_start(args->cmdline);
path = get_cpu0_qom_path();
- value = qobject_to_qnum(qom_get(path, args->property));
+ value = qobject_to(QNum, qom_get(path, args->property));
g_assert(qnum_get_try_int(value, &val));
g_assert_cmpint(val, ==, args->expected_value);
qtest_end();
@@ -105,7 +105,7 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx,
const QListEntry *e;
for (e = qlist_first(features); e; e = qlist_next(e)) {
- QDict *w = qobject_to_qdict(qlist_entry_obj(e));
+ QDict *w = qobject_to(QDict, qlist_entry_obj(e));
const char *rreg = qdict_get_str(w, "cpuid-register");
uint32_t reax = qdict_get_int(w, "cpuid-input-eax");
bool has_ecx = qdict_haskey(w, "cpuid-input-ecx");
@@ -116,8 +116,9 @@ static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx,
recx = qdict_get_int(w, "cpuid-input-ecx");
}
if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) {
- g_assert(qnum_get_try_int(qobject_to_qnum(qdict_get(w, "features")),
- &val));
+ g_assert(qnum_get_try_int(qobject_to(QNum,
+ qdict_get(w, "features")),
+ &val));
return val;
}
}
@@ -133,8 +134,8 @@ static void test_feature_flag(const void *data)
qtest_start(args->cmdline);
path = get_cpu0_qom_path();
- present = qobject_to_qlist(qom_get(path, "feature-words"));
- filtered = qobject_to_qlist(qom_get(path, "filtered-features"));
+ present = qobject_to(QList, qom_get(path, "feature-words"));
+ filtered = qobject_to(QList, qom_get(path, "filtered-features"));
value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg);
value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg);
qtest_end();
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 7393d69bb2..037872bb98 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -216,6 +216,9 @@ static void test_unaligned_write_same(void)
const uint8_t write_same_cdb_2[VIRTIO_SCSI_CDB_SIZE] = {
0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00
};
+ const uint8_t write_same_cdb_ndob[VIRTIO_SCSI_CDB_SIZE] = {
+ 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00
+ };
vs = qvirtio_scsi_pci_init(PCI_SLOT);
@@ -225,6 +228,9 @@ static void test_unaligned_write_same(void)
g_assert_cmphex(0, ==,
virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL));
+ g_assert_cmphex(0, ==,
+ virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL));
+
qvirtio_scsi_pci_free(vs);
}
diff --git a/trace-events b/trace-events
index 2c3e3d7690..ed71f44649 100644
--- a/trace-events
+++ b/trace-events
@@ -47,6 +47,9 @@ monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
handle_hmp_command(void *mon, const char *cmdline) "mon %p cmdline: %s"
handle_qmp_command(void *mon, const char *req) "mon %p req: %s"
+monitor_suspend(void *ptr, int cnt) "mon %p: %d"
+monitor_qmp_cmd_in_band(const char *id) "%s"
+monitor_qmp_cmd_out_of_band(const char *id) "%s"
# dma-helpers.c
dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d"
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 18de0bb3ea..110b393e4e 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1330,7 +1330,7 @@ QemuCocoaView *cocoaView;
/* Create the version string*/
NSString *version_string;
version_string = [[NSString alloc] initWithFormat:
- @"QEMU emulator version %s%s", QEMU_VERSION, QEMU_PKGVERSION];
+ @"QEMU emulator version %s", QEMU_FULL_VERSION];
[version_label setStringValue: version_string];
[superView addSubview: version_label];
diff --git a/ui/console.c b/ui/console.c
index a8868fc04f..530a491987 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1282,11 +1282,16 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
s->console_type = console_type;
consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1));
- if (console_type != GRAPHIC_CONSOLE) {
+ if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
s->index = nb_consoles;
consoles[nb_consoles++] = s;
} else {
- /* HACK: Put graphical consoles before text consoles. */
+ /*
+ * HACK: Put graphical consoles before text consoles.
+ *
+ * Only do that for coldplugged devices. After initial device
+ * initialization we will not renumber the consoles any more.
+ */
for (i = nb_consoles; i > 0; i--) {
if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
break;
@@ -1874,21 +1879,61 @@ QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
int height = 480;
QemuConsole *s;
DisplayState *ds;
+ DisplaySurface *surface;
ds = get_alloc_displaystate();
- trace_console_gfx_new();
- s = new_console(ds, GRAPHIC_CONSOLE, head);
- s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s);
+ s = qemu_console_lookup_unused();
+ if (s) {
+ trace_console_gfx_reuse(s->index);
+ if (s->surface) {
+ width = surface_width(s->surface);
+ height = surface_height(s->surface);
+ }
+ } else {
+ trace_console_gfx_new();
+ s = new_console(ds, GRAPHIC_CONSOLE, head);
+ s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ dpy_set_ui_info_timer, s);
+ }
graphic_console_set_hwops(s, hw_ops, opaque);
if (dev) {
object_property_set_link(OBJECT(s), OBJECT(dev), "device",
&error_abort);
}
- s->surface = qemu_create_message_surface(width, height, noinit);
+ surface = qemu_create_message_surface(width, height, noinit);
+ dpy_gfx_replace_surface(s, surface);
return s;
}
+static const GraphicHwOps unused_ops = {
+ /* no callbacks */
+};
+
+void graphic_console_close(QemuConsole *con)
+{
+ static const char unplugged[] =
+ "Guest display has been unplugged";
+ DisplaySurface *surface;
+ int width = 640;
+ int height = 480;
+
+ if (con->surface) {
+ width = surface_width(con->surface);
+ height = surface_height(con->surface);
+ }
+
+ trace_console_gfx_close(con->index);
+ object_property_set_link(OBJECT(con), NULL, "device", &error_abort);
+ graphic_console_set_hwops(con, &unused_ops, NULL);
+
+ if (con->gl) {
+ dpy_gl_scanout_disable(con);
+ }
+ surface = qemu_create_message_surface(width, height, unplugged);
+ dpy_gfx_replace_surface(con, surface);
+}
+
QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{
if (index >= nb_consoles) {
@@ -1945,6 +1990,28 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
return con;
}
+QemuConsole *qemu_console_lookup_unused(void)
+{
+ Object *obj;
+ int i;
+
+ for (i = 0; i < nb_consoles; i++) {
+ if (!consoles[i]) {
+ continue;
+ }
+ if (consoles[i]->hw_ops != &unused_ops) {
+ continue;
+ }
+ obj = object_property_get_link(OBJECT(consoles[i]),
+ "device", &error_abort);
+ if (obj != NULL) {
+ continue;
+ }
+ return consoles[i];
+ }
+ return NULL;
+}
+
bool qemu_console_is_visible(QemuConsole *con)
{
return (con == active_console) || (con->dcls > 0);
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 6e591ab821..3e52abd92d 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -6,6 +6,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
+#include "standard-headers/drm/drm_fourcc.h"
PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format)
{
@@ -88,6 +89,27 @@ pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian)
return 0;
}
+/* Note: drm is little endian, pixman is native endian */
+pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format)
+{
+ static const struct {
+ uint32_t drm_format;
+ pixman_format_code_t pixman;
+ } map[] = {
+ { DRM_FORMAT_RGB888, PIXMAN_LE_r8g8b8 },
+ { DRM_FORMAT_ARGB8888, PIXMAN_LE_a8r8g8b8 },
+ { DRM_FORMAT_XRGB8888, PIXMAN_LE_x8r8g8b8 }
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(map); i++) {
+ if (drm_format == map[i].drm_format) {
+ return map[i].pixman;
+ }
+ }
+ return 0;
+}
+
int qemu_pixman_get_type(int rshift, int gshift, int bshift)
{
int type = PIXMAN_TYPE_OTHER;
diff --git a/ui/trace-events b/ui/trace-events
index a957f363f1..eb4bf7f00d 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -2,6 +2,8 @@
# ui/console.c
console_gfx_new(void) ""
+console_gfx_reuse(int index) "%d"
+console_gfx_close(int index) "%d"
console_putchar_csi(int esc_param0, int esc_param1, int ch, int nb_esc_params) "escape sequence CSI%d;%d%c, %d parameters"
console_putchar_unhandled(int ch) "unhandled escape character '%c'"
console_txt_new(int w, int h) "%dx%d"
diff --git a/util/Makefile.objs b/util/Makefile.objs
index ae90b9963d..728c3541db 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -33,6 +33,7 @@ util-obj-y += throttle.o
util-obj-y += getauxval.o
util-obj-y += readline.o
util-obj-y += rcu.o
+util-obj-$(CONFIG_MEMBARRIER) += sys_membarrier.o
util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
util-obj-y += qemu-coroutine-sleep.o
util-obj-y += coroutine-$(CONFIG_COROUTINE_BACKEND).o
diff --git a/util/cutils.c b/util/cutils.c
index b33ede83d1..0de69e6db4 100644
--- a/util/cutils.c
+++ b/util/cutils.c
@@ -298,6 +298,115 @@ static int check_strtox_error(const char *nptr, char *ep,
}
/**
+ * Convert string @nptr to an integer, and store it in @result.
+ *
+ * This is a wrapper around strtol() that is harder to misuse.
+ * Semantics of @nptr, @endptr, @base match strtol() with differences
+ * noted below.
+ *
+ * @nptr may be null, and no conversion is performed then.
+ *
+ * If no conversion is performed, store @nptr in *@endptr and return
+ * -EINVAL.
+ *
+ * If @endptr is null, and the string isn't fully converted, return
+ * -EINVAL. This is the case when the pointer that would be stored in
+ * a non-null @endptr points to a character other than '\0'.
+ *
+ * If the conversion overflows @result, store INT_MAX in @result,
+ * and return -ERANGE.
+ *
+ * If the conversion underflows @result, store INT_MIN in @result,
+ * and return -ERANGE.
+ *
+ * Else store the converted value in @result, and return zero.
+ */
+int qemu_strtoi(const char *nptr, const char **endptr, int base,
+ int *result)
+{
+ char *ep;
+ long long lresult;
+
+ if (!nptr) {
+ if (endptr) {
+ *endptr = nptr;
+ }
+ return -EINVAL;
+ }
+
+ errno = 0;
+ lresult = strtoll(nptr, &ep, base);
+ if (lresult < INT_MIN) {
+ *result = INT_MIN;
+ errno = ERANGE;
+ } else if (lresult > INT_MAX) {
+ *result = INT_MAX;
+ errno = ERANGE;
+ } else {
+ *result = lresult;
+ }
+ return check_strtox_error(nptr, ep, endptr, errno);
+}
+
+/**
+ * Convert string @nptr to an unsigned integer, and store it in @result.
+ *
+ * This is a wrapper around strtoul() that is harder to misuse.
+ * Semantics of @nptr, @endptr, @base match strtoul() with differences
+ * noted below.
+ *
+ * @nptr may be null, and no conversion is performed then.
+ *
+ * If no conversion is performed, store @nptr in *@endptr and return
+ * -EINVAL.
+ *
+ * If @endptr is null, and the string isn't fully converted, return
+ * -EINVAL. This is the case when the pointer that would be stored in
+ * a non-null @endptr points to a character other than '\0'.
+ *
+ * If the conversion overflows @result, store UINT_MAX in @result,
+ * and return -ERANGE.
+ *
+ * Else store the converted value in @result, and return zero.
+ *
+ * Note that a number with a leading minus sign gets converted without
+ * the minus sign, checked for overflow (see above), then negated (in
+ * @result's type). This is exactly how strtoul() works.
+ */
+int qemu_strtoui(const char *nptr, const char **endptr, int base,
+ unsigned int *result)
+{
+ char *ep;
+ long long lresult;
+
+ if (!nptr) {
+ if (endptr) {
+ *endptr = nptr;
+ }
+ return -EINVAL;
+ }
+
+ errno = 0;
+ lresult = strtoull(nptr, &ep, base);
+
+ /* Windows returns 1 for negative out-of-range values. */
+ if (errno == ERANGE) {
+ *result = -1;
+ } else {
+ if (lresult > UINT_MAX) {
+ *result = UINT_MAX;
+ errno = ERANGE;
+ } else if (lresult < INT_MIN) {
+ *result = UINT_MAX;
+ errno = ERANGE;
+ } else {
+ *result = lresult;
+ }
+ }
+ return check_strtox_error(nptr, ep, endptr, errno);
+}
+
+/**
* Convert string @nptr to a long integer, and store it in @result.
*
* This is a wrapper around strtol() that is harder to misuse.
diff --git a/util/keyval.c b/util/keyval.c
index 212ae90d00..1c7351a233 100644
--- a/util/keyval.c
+++ b/util/keyval.c
@@ -221,7 +221,7 @@ static const char *keyval_parse_one(QDict *qdict, const char *params,
if (!next) {
return NULL;
}
- cur = qobject_to_qdict(next);
+ cur = qobject_to(QDict, next);
assert(cur);
}
@@ -314,7 +314,7 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp)
has_member = true;
}
- qdict = qobject_to_qdict(ent->value);
+ qdict = qobject_to(QDict, ent->value);
if (!qdict) {
continue;
}
diff --git a/util/main-loop.c b/util/main-loop.c
index 7558eb5f53..992f9b0f34 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -29,6 +29,7 @@
#include "qemu/sockets.h" // struct in_addr needed for libslirp.h
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
+#include "sysemu/replay.h"
#include "slirp/libslirp.h"
#include "qemu/main-loop.h"
#include "block/aio.h"
@@ -245,18 +246,19 @@ static int os_host_main_loop_wait(int64_t timeout)
timeout = SCALE_MS;
}
+
if (timeout) {
spin_counter = 0;
- qemu_mutex_unlock_iothread();
} else {
spin_counter++;
}
+ qemu_mutex_unlock_iothread();
+ replay_mutex_unlock();
ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout);
- if (timeout) {
- qemu_mutex_lock_iothread();
- }
+ replay_mutex_lock();
+ qemu_mutex_lock_iothread();
glib_pollfds_poll();
@@ -463,8 +465,13 @@ static int os_host_main_loop_wait(int64_t timeout)
poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout);
qemu_mutex_unlock_iothread();
+
+ replay_mutex_unlock();
+
g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns);
+ replay_mutex_lock();
+
qemu_mutex_lock_iothread();
if (g_poll_ret > 0) {
for (i = 0; i < w->num; i++) {
diff --git a/util/qemu-config.c b/util/qemu-config.c
index c651c4826e..20f7d1429d 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -528,7 +528,7 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts,
}
QLIST_FOREACH_ENTRY(list, list_entry) {
- QDict *section = qobject_to_qdict(qlist_entry_obj(list_entry));
+ QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry));
char *opt_name;
if (!section) {
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 2b412eff5e..d0756fda58 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -919,15 +919,15 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
switch (qobject_type(obj)) {
case QTYPE_QSTRING:
- value = qstring_get_str(qobject_to_qstring(obj));
+ value = qstring_get_str(qobject_to(QString, obj));
break;
case QTYPE_QNUM:
- tmp = qnum_to_string(qobject_to_qnum(obj));
+ tmp = qnum_to_string(qobject_to(QNum, obj));
value = tmp;
break;
case QTYPE_QBOOL:
pstrcpy(buf, sizeof(buf),
- qbool_get_bool(qobject_to_qbool(obj)) ? "on" : "off");
+ qbool_get_bool(qobject_to(QBool, obj)) ? "on" : "off");
value = buf;
break;
default:
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 7f13e8a338..8bd8bb64eb 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -91,6 +91,14 @@ NetworkAddressFamily inet_netfamily(int family)
return NETWORK_ADDRESS_FAMILY_UNKNOWN;
}
+bool fd_is_socket(int fd)
+{
+ int optval;
+ socklen_t optlen = sizeof(optval);
+ return !qemu_getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen);
+}
+
+
/*
* Matrix we're trying to apply
*
@@ -1034,6 +1042,30 @@ fail:
return NULL;
}
+static int socket_get_fd(const char *fdstr, Error **errp)
+{
+ int fd;
+ if (cur_mon) {
+ fd = monitor_get_fd(cur_mon, fdstr, errp);
+ if (fd < 0) {
+ return -1;
+ }
+ } else {
+ if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) {
+ error_setg_errno(errp, errno,
+ "Unable to parse FD number %s",
+ fdstr);
+ return -1;
+ }
+ }
+ if (!fd_is_socket(fd)) {
+ error_setg(errp, "File descriptor '%s' is not a socket", fdstr);
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
int socket_connect(SocketAddress *addr, Error **errp)
{
int fd;
@@ -1048,7 +1080,7 @@ int socket_connect(SocketAddress *addr, Error **errp)
break;
case SOCKET_ADDRESS_TYPE_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
+ fd = socket_get_fd(addr->u.fd.str, errp);
break;
case SOCKET_ADDRESS_TYPE_VSOCK:
@@ -1075,7 +1107,7 @@ int socket_listen(SocketAddress *addr, Error **errp)
break;
case SOCKET_ADDRESS_TYPE_FD:
- fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
+ fd = socket_get_fd(addr->u.fd.str, errp);
break;
case SOCKET_ADDRESS_TYPE_VSOCK:
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 82d56507a2..2ed1bf2778 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -622,6 +622,18 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
}
}
+uint64_t qemu_clock_get_last(QEMUClockType type)
+{
+ QEMUClock *clock = qemu_clock_ptr(type);
+ return clock->last;
+}
+
+void qemu_clock_set_last(QEMUClockType type, uint64_t last)
+{
+ QEMUClock *clock = qemu_clock_ptr(type);
+ clock->last = last;
+}
+
void qemu_clock_register_reset_notifier(QEMUClockType type,
Notifier *notifier)
{
diff --git a/util/rcu.c b/util/rcu.c
index f4d09c8304..5676c22bd1 100644
--- a/util/rcu.c
+++ b/util/rcu.c
@@ -92,10 +92,11 @@ static void wait_for_readers(void)
atomic_set(&index->waiting, true);
}
- /* Here, order the stores to index->waiting before the
- * loads of index->ctr.
+ /* Here, order the stores to index->waiting before the loads of
+ * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(),
+ * ensuring that the loads of index->ctr are sequentially consistent.
*/
- smp_mb();
+ smp_mb_global();
QLIST_FOREACH_SAFE(index, &registry, node, tmp) {
if (!rcu_gp_ongoing(&index->ctr)) {
@@ -142,8 +143,13 @@ static void wait_for_readers(void)
void synchronize_rcu(void)
{
qemu_mutex_lock(&rcu_sync_lock);
- qemu_mutex_lock(&rcu_registry_lock);
+ /* Write RCU-protected pointers before reading p_rcu_reader->ctr.
+ * Pairs with smp_mb_placeholder() in rcu_read_lock().
+ */
+ smp_mb_global();
+
+ qemu_mutex_lock(&rcu_registry_lock);
if (!QLIST_EMPTY(&registry)) {
/* In either case, the atomic_mb_set below blocks stores that free
* old RCU-protected pointers.
@@ -370,6 +376,7 @@ static void rcu_init_child(void)
static void __attribute__((__constructor__)) rcu_init(void)
{
+ smp_mb_global_init();
#ifdef CONFIG_POSIX
pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child);
#endif
diff --git a/util/sys_membarrier.c b/util/sys_membarrier.c
new file mode 100644
index 0000000000..8dcb53e63e
--- /dev/null
+++ b/util/sys_membarrier.c
@@ -0,0 +1,50 @@
+/*
+ * Process-global memory barriers
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ */
+
+#include <qemu/osdep.h>
+#include <qemu/sys_membarrier.h>
+#include <qemu/error-report.h>
+
+#ifdef CONFIG_LINUX
+#include <linux/membarrier.h>
+#include <sys/syscall.h>
+
+static int
+membarrier(int cmd, int flags)
+{
+ return syscall(__NR_membarrier, cmd, flags);
+}
+#endif
+
+void smp_mb_global(void)
+{
+#if defined CONFIG_WIN32
+ FlushProcessWriteBuffers();
+#elif defined CONFIG_LINUX
+ membarrier(MEMBARRIER_CMD_SHARED, 0);
+#else
+#error --enable-membarrier is not supported on this operating system.
+#endif
+}
+
+void smp_mb_global_init(void)
+{
+#ifdef CONFIG_LINUX
+ int ret = membarrier(MEMBARRIER_CMD_QUERY, 0);
+ if (ret < 0) {
+ error_report("This QEMU binary requires the membarrier system call.");
+ error_report("Please upgrade your system to a newer version of Linux");
+ exit(1);
+ }
+ if (!(ret & MEMBARRIER_CMD_SHARED)) {
+ error_report("This QEMU binary requires MEMBARRIER_CMD_SHARED support.");
+ error_report("Please upgrade your system to a newer version of Linux");
+ exit(1);
+ }
+#endif
+}
diff --git a/vl.c b/vl.c
index 0b1581107f..c81cc86607 100644
--- a/vl.c
+++ b/vl.c
@@ -1949,7 +1949,7 @@ static void main_loop(void)
static void version(void)
{
- printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION "\n"
+ printf("QEMU emulator version " QEMU_FULL_VERSION "\n"
QEMU_COPYRIGHT "\n");
}
@@ -2697,7 +2697,7 @@ static void qemu_run_exit_notifiers(void)
notifier_list_notify(&exit_notifiers, NULL);
}
-static bool machine_init_done;
+bool machine_init_done;
void qemu_add_machine_init_done_notifier(Notifier *notify)
{
@@ -2714,8 +2714,8 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify)
static void qemu_run_machine_init_done_notifiers(void)
{
- notifier_list_notify(&machine_init_done_notifiers, NULL);
machine_init_done = true;
+ notifier_list_notify(&machine_init_done_notifiers, NULL);
}
static const QEMUOption *lookup_opt(int argc, char **argv,
@@ -3059,6 +3059,7 @@ int main(int argc, char **argv, char **envp)
qemu_init_cpu_list();
qemu_init_cpu_loop();
+
qemu_mutex_lock_iothread();
atexit(qemu_run_exit_notifiers);
@@ -3066,7 +3067,6 @@ int main(int argc, char **argv, char **envp)
qemu_init_exec_dir(argv[0]);
module_call_init(MODULE_INIT_QOM);
- monitor_init_qmp_commands();
qemu_add_opts(&qemu_drive_opts);
qemu_add_drive_opts(&qemu_legacy_drive_opts);
@@ -4504,6 +4504,7 @@ int main(int argc, char **argv, char **envp)
blk_mig_init();
ram_mig_init();
+ dirty_bitmap_mig_init();
/* If the currently selected machine wishes to override the units-per-bus
* property of its default HBA interface type, do so now. */
@@ -4537,6 +4538,12 @@ int main(int argc, char **argv, char **envp)
default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
+ /*
+ * Note: qtest_enabled() (which is used in monitor_qapi_event_init())
+ * depends on configure_accelerator() above.
+ */
+ monitor_init_globals();
+
if (qemu_opts_foreach(qemu_find_opts("mon"),
mon_init_func, NULL, NULL)) {
exit(1);
@@ -4584,15 +4591,11 @@ int main(int argc, char **argv, char **envp)
current_machine->maxram_size = maxram_size;
current_machine->ram_slots = ram_slots;
current_machine->boot_order = boot_order;
- current_machine->cpu_model = cpu_model;
/* parse features once if machine provides default cpu_type */
- if (machine_class->default_cpu_type) {
- current_machine->cpu_type = machine_class->default_cpu_type;
- if (cpu_model) {
- current_machine->cpu_type =
- cpu_parse_cpu_model(machine_class->default_cpu_type, cpu_model);
- }
+ current_machine->cpu_type = machine_class->default_cpu_type;
+ if (cpu_model) {
+ current_machine->cpu_type = parse_cpu_model(cpu_model);
}
parse_numa_opts(current_machine);